[m-rev.] for review: analysis framework (1/2)

Peter Wang wangp at students.cs.mu.OZ.AU
Mon Jan 23 14:01:53 AEDT 2006


On 2006-01-16, Julien Fischer <juliensf at cs.mu.OZ.AU> wrote:
> 
> On Mon, 16 Jan 2006, Peter Wang wrote:
> 
> > Estimated hours taken: 30
> > Branches: main
> >
> > Some work on the intermodule analysis framework.  The main changes are that
> > modules and analysis results have statuses associated with them, which are
> > saved into the `.analysis' files, and there is now code to handle intermodule
> 
> s/in/into/

s/into/in ?

> 
> > dependency graphs (that record which modules are dependent on a particular
> > analysis result).
> >
> > Automatic recompilation of modules that use out of date or invalid analysis
> > results from other modules is not handled yet.
> >
> > analysis/analysis.m:
> > analysis/analysis.file.m:
> > 	Remove the `FuncInfo' type variable everywhere.  This was originally
> > 	designed to be used by analyses to store "extra" information that
> > 	would be passed from an analysis implementation through the analysis
> > 	framework, back to methods defined by the analysis implementation
> > 	itself.  One problem was that `FuncInfo' values were not designed to
> > 	be saved and restored from disk.
> 
> Presumably that could be dealt with by insisting that they be members of the
> to_string typeclass though?

Yes.

> 
> >       Also, it made two `Call' or two
> > 	`Answer' values hard to compare, as a `FuncInfo' value had to be
> > 	present for a comparison call to be made, and it was not always
> > 	obvious where that `FuncInfo' value would come from.  I have changed
> > 	it so that that any information which might be be stored in a
> > 	`FuncInfo' should be stored in the corresponding `Call' value itself.
> >
> 
> I don't understand this one; could you provide an example.

The methods in the partial_order typeclass both require a FuncInfo value so
to compare any two Call (or Answer) values you need to get a FuncInfo value
from somewhere.  It is usually supplied by the analysis pass which is
invoking the framework.

Where this is hard is when we are ready to write everything out to disk at
the end of all analyses.  The analysis results have been recorded in a map,
and you have the IMDG for the current module.  For each analysis result you
need to compare new Answers with old Answers, for which you need a FuncInfo,
which would have to be stored in the map as well.  Then you need to look up
the IMDG to see if another module was using the an old result, which involves
comparing Call patterns, which also requires a FuncInfo from somewhere.
Any time you have two FuncInfos, you need either to add partial_order methods
to take two FuncInfos, or you choose one of the FuncInfos arbitrarily.

Since FuncInfos would have to be read/written with every Call and Answer
value, including IMDGs, I decided that analyses might as well put whatever
information that would be in a FuncInfo into the Call or Answer as it likes.
Nick's thesis only deals in call and answer patterns and the unused args
code just uses the FuncInfo to store the arity so that it can work out
the representation of the `bottom' answer (which isn't used at all so far).
Unless it will be useful for many other analyses, I would really like to
be rid of the FuncInfos.

> > compiler/mercury_compile.m:
> > 	Update to match changes in the intermodule analysis framework.
> >
> > compiler/mmc_analysis.m:
> > 	Add the trail usage analysis to the list of analyses to be used with
> > 	the intermodule analysis framework.
> > 	Update the entry for unused argument elimination.
> 
> At the moment the results from the intermodule analysis framework should
> correspond to the pragmas in the .opt files when compiling with
> --intermodule-optimization (although not with
> --transitive-intermodule-optimization).
> You should check that this is the case.

It mostly does, although the results can be different due to the order
of compilation because the .analysis files are currently written out as
a byproduct of doing other things.  The trail usage analysis was writing
out results for non-exported procedures as well; I've fixed that now.

> > @@ -163,49 +231,122 @@
> >  	throw(invalid_analysis_file)
> >      ).
> >
> > +read_module_imdg(Info, ModuleId, ModuleEntries, !IO) :-
> > +    read_analysis_file(Info ^ compiler, ModuleId, ".imdg",
> 
> Make sure that mmake realclean (clean?) and the corresponding functionality in
> mmc --make know how to clean up any new filetypes you add.

Done.  It won't clean up the .analysis, .imdg, and .request files for
imported library modules though.  Those should probably not be written
in the first place, but I'm leaving that for later.

> 
> > +record_dependency(CallerModuleId, AnalysisName, CalleeModuleId, FuncId, Call,
> > +	!Info) :-
> > +    (if CallerModuleId = CalleeModuleId then
> > +	% XXX this assertion breaks compiling the standard library with
> > +	% --analyse-trail-usage at the moment
> 
> How is it breaking it?

record_dependency is being called with CalleeModuleId = CallerModuleId
for some procedures during trail usage analysis.  I haven't figured it
out as yet.

> > +% XXX make this enableable with a command-line option.  A problem is that we
> > +% don't want to make the analysis directory dependent on anything in the
> > +% compiler directory.
> > +
> > +:- pred debug_msg(pred(io, io)::in(pred(di, uo) is det), io::di, io::uo)
> > +    is det.
> > +
> > +debug_msg(_P, !IO) :-
> > +    % P(!IO),
> > +    true.
> 
> I suggest using a mutable to keep track of whether debugging traces
> are enabled and then export a predicate from the analysis library that
> client compilers can use to turn debugging on and off.

Are mutables supported in non-C backends yet?


I've followed the rest of your suggestions and fixed a few bugs.
The interdiff follows.


--- analysis.txt_old	2006-01-20 16:06:16.000000000 +1100
+++ analysis.txt	2006-01-23 13:49:47.000000000 +1100
@@ -1,15 +1,16 @@
-Estimated hours taken: 30
+Estimated hours taken: 45
 Branches: main
 
 Some work on the intermodule analysis framework.  The main changes are that
 modules and analysis results have statuses associated with them, which are
-saved into the `.analysis' files, and there is now code to handle intermodule
+saved in the `.analysis' files, and there is now code to handle intermodule
 dependency graphs (that record which modules are dependent on a particular
 analysis result).
 
 Automatic recompilation of modules that use out of date or invalid analysis
 results from other modules is not handled yet.
 
+analysis/README:
 analysis/analysis.m:
 analysis/analysis.file.m:
 	Remove the `FuncInfo' type variable everywhere.  This was originally
@@ -38,19 +39,23 @@
 	with earlier versions.
 
 compiler/mercury_compile.m:
-	Update to match changes in the intermodule analysis framework.
+	Make `mercury_compile_after_front_end' use state variables for
+	copies of the HLDS and update it to match changes in the intermodule
+	analysis framework.
 
 compiler/mmc_analysis.m:
 	Add the trail usage analysis to the list of analyses to be used with
 	the intermodule analysis framework.
+
 	Update the entry for unused argument elimination.
 
+	Add predicate `module_id_func_id'.
+
 compiler/add_pragma.m:
 compiler/hlds_module.m:
 compiler/trailing_analysis.m:
 	Make the trail usage analysis pass able to make use of the intermodule
-	analysis framework.  Mainly, functions had to be converted to predicates
-	taking I/O states.
+	analysis framework.
 
 	Associate each `trailing_status' in the `trailing_info' map with an
 	`analysis_status', i.e. whether it is optimal or not.
@@ -66,6 +71,21 @@
 	Record the dependence of the current module on analysis results from
 	other modules.
 
+compiler/goal_util.m:
+	Add predicate `pred_proc_ids_from_goal/2'.
+
+compiler/make.m:
+compiler/make.dependencies.m:
+compiler/make.program_target.m:
+compiler/make.util.m:
+compiler/modules.m:
+scripts/Mmake.vars.in:
+	Make `mmc --make' and `mmake' realclean `.analysis', `.imdg' and
+	`.request' files of non-library modules.  Files for imported library
+	modules won't be deleted, but they probably shouldn't be generated in
+	the first place.
+
+NEWS:
 library/list.m:
 	Add a `list.map2_foldl2' predicate.
 
diff -u analysis/analysis.file.m analysis/analysis.file.m
--- analysis/analysis.file.m	10 Jan 2006 04:02:34 -0000
+++ analysis/analysis.file.m	17 Jan 2006 00:29:15 -0000
@@ -104,7 +104,6 @@
 		( analysis_status_to_string(Status0, String) ->
 			Status = Status0
 		;
-			error("read_module_status: unknown status " ++ String),
 			throw(invalid_analysis_file)
 		)
 	;
@@ -283,14 +282,15 @@
 
 %-----------------------------------------------------------------------------%
 
-:- type read_header(T) == pred(T, io, io).
-:- inst read_header == (pred(out, di, uo) is det).
+:- type read_analysis_header(T) == pred(T, io, io).
+:- inst read_analysis_header == (pred(out, di, uo) is det).
 
 :- type parse_entry(T) == pred(term, T, T).
 :- inst parse_entry == (pred(in, in, out) is det).
 
 :- pred read_analysis_file(Compiler::in, module_id::in, string::in,
-		read_header(Header)::in(read_header), Header::in, Header::out,
+		read_analysis_header(Header)::in(read_analysis_header),
+		Header::in, Header::out,
 		parse_entry(T)::in(parse_entry), T::in, T::out,
 		io__state::di, io__state::uo) is det <= compiler(Compiler).
 
diff -u analysis/analysis.m analysis/analysis.m
--- analysis/analysis.m	10 Jan 2006 04:33:53 -0000
+++ analysis/analysis.m	19 Jan 2006 06:08:27 -0000
@@ -243,15 +243,16 @@
 		old_analysis_results :: analysis_map(analysis_result),
 		new_analysis_results :: analysis_map(analysis_result),
 
-			% The Inter-module Dependency Graph records dependences
-			% of an entire module's analysis results on another
-			% module's answer patterns. e.g. assume module M1
-			% contains function F1 that has an analysis result that
-			% used the answer F2:CP2->AP2 from module M2.  If AP2
-			% changes then all of M1 will either be marked
-			% `suboptimal' or `invalid'.  Finer-grained dependency
-			% tracking would allow only F1 to be recompiled,
-			% instead of all of M1, but we don't do that.
+			% The Inter-module Dependency Graph records
+			% dependencies of an entire module's analysis results
+			% on another module's answer patterns. e.g. assume
+			% module M1 contains function F1 that has an analysis
+			% result that used the answer F2:CP2->AP2 from module
+			% M2.  If AP2 changes then all of M1 will either be
+			% marked `suboptimal' or `invalid'.  Finer-grained
+			% dependency tracking would allow only F1 to be
+			% recompiled, instead of all of M1, but we don't do
+			% that.
 			%
 			% IMDGs are loaded from disk into the old map.
 			% During analysis any dependences of the current module
@@ -518,7 +519,7 @@
 	%    CallerModuleId ++ " must be different")
 	true
     else
-	( Analyses0 = map.search(!.Info ^ new_imdg, CallerModuleId) ->
+	( Analyses0 = map.search(!.Info ^ new_imdg, CalleeModuleId) ->
 	    Analyses1 = Analyses0
 	;
 	    Analyses1 = map.init
@@ -533,12 +534,17 @@
 	;
 	    FuncArcs1 = []
 	),
-	!:Info = !.Info ^ new_imdg :=
-	    map.set(!.Info ^ new_imdg, CalleeModuleId,
-	    map.set(Analyses1, AnalysisName,
-	    map.set(Funcs1, FuncId, FuncArcs))),
-	FuncArcs = [Dep | FuncArcs1],
-	Dep = 'new imdg_arc'(Call, CallerModuleId)
+	Dep = 'new imdg_arc'(Call, CallerModuleId),
+	% XXX this should really be a set to begin with
+	( Dep `list.member` FuncArcs1 ->
+	    true
+	;
+	    !:Info = !.Info ^ new_imdg :=
+		map.set(!.Info ^ new_imdg, CalleeModuleId,
+		map.set(Analyses1, AnalysisName,
+		map.set(Funcs1, FuncId, FuncArcs))),
+	    FuncArcs = [Dep | FuncArcs1]
+	)
     ).
 
 %-----------------------------------------------------------------------------%
@@ -546,6 +552,7 @@
 
     % The algorithm is from Nick's thesis, pp. 108-9.
     % Or my corruption thereof.
+    % See the `analysis/README' file for a reference.
     % 
     % For each new analysis result (P^M:DP --> Ans_new):
     %   Read in the registry of M if necessary
@@ -583,6 +590,7 @@
 	analysis_info::in, analysis_info::out, io::di, io::uo) is det.
 
 update_analysis_registry(!Info, !IO) :-
+    debug_msg(io.print("Updating analysis registry.\n"), !IO),
     map.foldl2(update_analysis_registry_2, !.Info ^ new_analysis_results,
 	!Info, !IO),
     !:Info = !.Info ^ new_analysis_results := map.init.
@@ -598,10 +606,12 @@
 
 update_analysis_registry_4(ModuleId, AnalysisName, FuncId, NewResults,
 	!Info, !IO) :-
+    % XXX Currently we do not prevent there being more than one recorded result
+    % for a given call pattern.
     list.foldl2(update_analysis_registry_5(ModuleId, AnalysisName, FuncId),
 	NewResults, !Info, !IO).
 
-update_analysis_registry_5(ModuleId, _AnalysisName, FuncId, NewResult,
+update_analysis_registry_5(ModuleId, AnalysisName, FuncId, NewResult,
 	!Info, !IO) :-
     NewResult = analysis_result(Call, NewAnswer, NewStatus),
     lookup_exactly_matching_result(ModuleId, FuncId, Call, MaybeResult,
@@ -651,6 +661,10 @@
 		Status = invalid
 	    ),
 	    debug_msg((pred(!.IO::di, !:IO::uo) is det :-
+		io.print(OldAnswer, !IO),
+		io.print(" changed to ", !IO),
+		io.print(NewAnswer, !IO),
+		io.nl(!IO),
 		io.print("Mark dependent modules as ", !IO),
 		io.print(Status, !IO),
 		io.nl(!IO),
@@ -659,7 +673,8 @@
 		io.nl(!IO)
 	    ), !IO),
 	    DepModules = imdg_dependent_modules(
-		!.Info ^ old_imdg ^ det_elem(ModuleId)),
+		!.Info ^ old_imdg ^ det_elem(ModuleId), AnalysisName,
+		FuncId, Call),
 	    set.fold2(taint_module_overall_status(Status), DepModules,
 		!Info, !IO)
 	)
@@ -716,25 +731,25 @@
 	replace_result_in_list(Call, Answer, Status, T0, T)
     ).
 
-    % Return the set of M
-    % where (M:CP --> _:_) `in` IMDG
-    %
-:- func imdg_dependent_modules(module_analysis_map(imdg_arc)) = set(module_id).
-:- func imdg_dependent_modules_2(analysis_name, func_analysis_map(imdg_arc),
-        set(module_id)) = set(module_id).
-:- func imdg_dependent_modules_3(func_id, list(imdg_arc),
-        set(module_id)) = set(module_id).
-
-imdg_dependent_modules(ModuleMap)
-    = map.foldl(imdg_dependent_modules_2, ModuleMap, set.init).
-imdg_dependent_modules_2(_AnalysisName, FuncMap, Modules0)
-    = map.foldl(imdg_dependent_modules_3, FuncMap, Modules0).
-imdg_dependent_modules_3(_FuncId, IMDGEntries, Modules0)
-    = set.union(Modules0, set.from_list(list.map(arc_module_id, IMDGEntries))).
+:- func imdg_dependent_modules(module_analysis_map(imdg_arc), analysis_name,
+	func_id, Call) = set(module_id) <= call_pattern(Call).
+
+imdg_dependent_modules(ModuleMap, AnalysisName, FuncId, Call) =
+    (if map.search(ModuleMap, AnalysisName, FuncAnalysisMap),
+	map.search(FuncAnalysisMap, FuncId, IMDGEntries)
+    then
+	set.from_list(list.filter_map(arc_module_id(Call), IMDGEntries))
+    else
+	set.init
+    ).
 
-:- func arc_module_id(imdg_arc) = module_id.
+    % XXX: compiler aborts if the modes are removed
+:- func arc_module_id(Call::in, imdg_arc::in) = (module_id::out) is semidet
+    <= call_pattern(Call).
 
-arc_module_id(imdg_arc(_, ModuleId)) = ModuleId.
+arc_module_id(CallA, imdg_arc(CallB0, ModuleId)) = ModuleId :-
+    det_univ_to_type(univ(CallB0), CallB),
+    equivalent(CallA, CallB).
 
 :- pred taint_module_overall_status(analysis_status::in,
 	module_id::in, analysis_info::in, analysis_info::out,
@@ -887,7 +902,7 @@
     ),
 
     % Write the results for all the modules we know of.  For the
-    % module being compiled, its analysis results may have changed.
+    % module being compiled, the analysis results may have changed.
     % For other modules, their overall statuses may have changed.
     map.foldl(write_analysis_files_2(!.Info),
 	!.Info ^ old_analysis_results, !IO),
diff -u compiler/add_pragma.m compiler/add_pragma.m
--- compiler/add_pragma.m	13 Jan 2006 02:52:55 -0000
+++ compiler/add_pragma.m	18 Jan 2006 03:17:24 -0000
@@ -600,10 +600,8 @@
     ->
         module_info_get_trailing_info(!.ModuleInfo, TrailingInfo0),
         proc_id_to_int(ProcId, ModeNum),
-        % The `optimal' part is only used by --intermodule-analysis
-        % whereas this is for --intermodule-optimisation.
-        % It should not matter what we put there.
-        map.set(TrailingInfo0, proc(PredId, ProcId), {TrailingStatus, optimal},
+        map.set(TrailingInfo0, proc(PredId, ProcId),
+            proc_trailing_info(TrailingStatus, no),
             TrailingInfo),
         module_info_set_trailing_info(TrailingInfo, !ModuleInfo)
     ;
diff -u compiler/hlds_module.m compiler/hlds_module.m
--- compiler/hlds_module.m	5 Jan 2006 00:37:17 -0000
+++ compiler/hlds_module.m	18 Jan 2006 03:17:31 -0000
@@ -107,7 +107,13 @@
     % Map from proc to an indication of whether or not it
     % modifies the trail.
     %
-:- type trailing_info == map(pred_proc_id, {trailing_status, analysis_status}).
+:- type trailing_info == map(pred_proc_id, proc_trailing_info).
+
+:- type proc_trailing_info
+    --->    proc_trailing_info(
+                proc_trailing_status        :: trailing_status,
+                proc_maybe_analysis_status  :: maybe(analysis_status)
+            ).
 
     % List of procedures for which there are user-requested type
     % specializations, and a list of predicates which should be
diff -u compiler/mercury_compile.m compiler/mercury_compile.m
--- compiler/mercury_compile.m	4 Jan 2006 07:15:00 -0000
+++ compiler/mercury_compile.m	17 Jan 2006 04:40:10 -0000
@@ -1483,13 +1483,12 @@
     list(string)::out, dump_info::in, dump_info::out, io::di, io::uo) is det.
 
 mercury_compile_after_front_end(NestedSubModules, FindTimestampFiles,
-        MaybeTimestamps, ModuleName, HLDS21, FactTableBaseFiles, !DumpInfo,
+        MaybeTimestamps, ModuleName, !.HLDS, FactTableBaseFiles, !DumpInfo,
         !IO) :-
     globals__io_lookup_bool_option(verbose, Verbose, !IO),
     globals__io_lookup_bool_option(statistics, Stats, !IO),
-    maybe_output_prof_call_graph(Verbose, Stats,
-        HLDS21, HLDS25, !IO),
-    middle_pass(ModuleName, HLDS25, HLDS50, !DumpInfo, !IO),
+    maybe_output_prof_call_graph(Verbose, Stats, !HLDS, !IO),
+    middle_pass(ModuleName, !HLDS, !DumpInfo, !IO),
     globals__io_lookup_bool_option(highlevel_code, HighLevelCode, !IO),
     globals__io_lookup_bool_option(aditi_only, AditiOnly, !IO),
     globals__io_get_target(Target, !IO),
@@ -1506,25 +1505,24 @@
     io__remove_file(UsageFileName, _, !IO),
 
     % magic sets can report errors.
-    module_info_get_num_errors(HLDS50, NumErrors),
+    module_info_get_num_errors(!.HLDS, NumErrors),
     ( NumErrors = 0 ->
         globals__io_lookup_bool_option(intermodule_analysis, IntermodAnalysis,
             !IO),
         (
             IntermodAnalysis = yes,
-            module_info_get_analysis_info(HLDS50, AnalysisInfo0),
-            module_info_get_all_deps(HLDS50, ImportedModules),
+            module_info_get_analysis_info(!.HLDS, AnalysisInfo0),
+            module_info_get_all_deps(!.HLDS, ImportedModules),
             ModuleId = module_name_to_module_id(ModuleName),
             ImportedModuleIds = set.map(module_name_to_module_id,
                 ImportedModules),
             analysis__write_analysis_files(ModuleId, ImportedModuleIds,
                 AnalysisInfo0, AnalysisInfo, !IO),
-            module_info_set_analysis_info(AnalysisInfo, HLDS50, HLDS50B)
+            module_info_set_analysis_info(AnalysisInfo, !HLDS)
         ;
-            IntermodAnalysis = no,
-            HLDS50B = HLDS50
+            IntermodAnalysis = no
         ),
-        maybe_generate_rl_bytecode(Verbose, MaybeRLFile, HLDS50B, HLDS51, !IO),
+        maybe_generate_rl_bytecode(Verbose, MaybeRLFile, !HLDS, !IO),
         (
             ( Target = c
             ; Target = asm
@@ -1535,17 +1533,15 @@
             % <module>.mh containing function prototypes
             % for the `:- pragma export'ed procedures.
             %
-            export__get_foreign_export_decls(HLDS50, ExportDecls),
+            export__get_foreign_export_decls(!.HLDS, ExportDecls),
             export__produce_header_file(ExportDecls, ModuleName, !IO)
         ;
             true
         ),
         ( AditiOnly = yes ->
-            HLDS = HLDS51,
             FactTableBaseFiles = []
         ; Target = il ->
-            HLDS = HLDS51,
-            mlds_backend(HLDS, _, MLDS, !DumpInfo, !IO),
+            mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
             (
                 TargetCodeOnly = yes,
                 mlds_to_il_assembler(MLDS, !IO)
@@ -1560,9 +1556,8 @@
             ),
             FactTableBaseFiles = []
         ; Target = java ->
-            HLDS = HLDS51,
-            mlds_backend(HLDS, _, MLDS, !DumpInfo, !IO),
-            mlds_to_java(HLDS, MLDS, !IO),
+            mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
+            mlds_to_java(!.HLDS, MLDS, !IO),
             (
                 TargetCodeOnly = yes
             ;
@@ -1577,8 +1572,7 @@
             FactTableBaseFiles = []
         ; Target = asm ->
             % compile directly to assembler using the gcc back-end
-            HLDS = HLDS51,
-            mlds_backend(HLDS, _, MLDS, !DumpInfo, !IO),
+            mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
             maybe_mlds_to_gcc(MLDS, MaybeRLFile, ContainsCCode, !IO),
             (
                 TargetCodeOnly = yes
@@ -1602,8 +1596,7 @@
             ),
             FactTableBaseFiles = []
         ; HighLevelCode = yes ->
-            HLDS = HLDS51,
-            mlds_backend(HLDS, _, MLDS, !DumpInfo, !IO),
+            mlds_backend(!.HLDS, _, MLDS, !DumpInfo, !IO),
             mlds_to_high_level_c(MLDS, MaybeRLFile, !IO),
             (
                 TargetCodeOnly = yes
@@ -1621,11 +1614,11 @@
             ),
             FactTableBaseFiles = []
         ;
-            backend_pass(HLDS51, HLDS, GlobalData, LLDS, !DumpInfo, !IO),
-            output_pass(HLDS, GlobalData, LLDS, MaybeRLFile, ModuleName,
+            backend_pass(!HLDS, GlobalData, LLDS, !DumpInfo, !IO),
+            output_pass(!.HLDS, GlobalData, LLDS, MaybeRLFile, ModuleName,
                 _CompileErrors, FactTableBaseFiles, !IO)
         ),
-        recompilation__usage__write_usage_file(HLDS, NestedSubModules,
+        recompilation__usage__write_usage_file(!.HLDS, NestedSubModules,
             MaybeTimestamps, !IO),
         FindTimestampFiles(ModuleName, TimestampFiles, !IO),
         list__foldl(touch_datestamp, TimestampFiles, !IO)
diff -u compiler/mmc_analysis.m compiler/mmc_analysis.m
--- compiler/mmc_analysis.m	15 Dec 2005 02:19:05 -0000
+++ compiler/mmc_analysis.m	17 Jan 2006 05:20:59 -0000
@@ -17,6 +17,7 @@
 :- interface.
 
 :- import_module analysis.
+:- import_module hlds.hlds_module.
 :- import_module hlds.hlds_pred.
 :- import_module mdbcomp.prim_data.
 :- import_module parse_tree.prog_data.
@@ -31,6 +32,9 @@
 :- func pred_or_func_name_arity_to_func_id(pred_or_func, string, arity,
     proc_id) = func_id.
 
+:- pred module_id_func_id(module_info::in, pred_proc_id::in,
+        module_id::out, func_id::out) is det.
+
 :- implementation.
 
 :- import_module parse_tree.modules.
@@ -46,7 +50,7 @@
 :- instance compiler(mmc) where [
     compiler_name(mmc) = "mmc",
 
-    analyses(mmc, "trailing_info") =
+    analyses(mmc, "trail_usage") =
         'new analysis_type'(
             unit1 `with_type` unit(any_call),
             unit1 `with_type` unit(trailing_analysis_answer)),
@@ -74,0 +79,10 @@
+
+module_id_func_id(ModuleInfo, proc(PredId, ProcId), ModuleId, FuncId) :-
+    module_info_pred_info(ModuleInfo, PredId, PredInfo),
+    PredModule = pred_info_module(PredInfo),
+    PredName = pred_info_name(PredInfo),
+    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
+    PredArity = pred_info_orig_arity(PredInfo),
+    ModuleId = module_name_to_module_id(PredModule),
+    FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
+        PredName, PredArity, ProcId).
diff -u compiler/trailing_analysis.m compiler/trailing_analysis.m
--- compiler/trailing_analysis.m	6 Jan 2006 02:21:39 -0000
+++ compiler/trailing_analysis.m	20 Jan 2006 00:15:48 -0000
@@ -165,9 +165,9 @@
 
 :- type proc_result
     --->    proc_result(
-                ppid            :: pred_proc_id,
-                status          :: trailing_status,
-                analysis_status :: analysis_status
+                ppid                    :: pred_proc_id,
+                status                  :: trailing_status,
+                maybe_analysis_status   :: maybe(analysis_status)
             ).
 
 :- pred process_scc(bool::in, bool::in, scc::in,
@@ -179,14 +179,14 @@
     % The `Results' above are the results of analysing each individual
     % procedure in the SCC - we now have to combine them in a meaningful way.   
     %
-    Status = combine_individual_proc_results(ProcResults),
-    AnalysisStatus = combine_individual_proc_statuses(ProcResults),
+    combine_individual_proc_results(ProcResults,
+        TrailingStatus, MaybeAnalysisStatus),
     %
     % Print out debugging information.
     %
     (
         Debug = yes,
-        dump_trail_usage_debug_info(!.ModuleInfo, SCC, Status, !IO)
+        dump_trail_usage_debug_info(!.ModuleInfo, SCC, TrailingStatus, !IO)
     ;
         Debug = no
     ),
@@ -195,7 +195,8 @@
     %
     module_info_get_trailing_info(!.ModuleInfo, TrailingInfo0),
     Update = (pred(PPId::in, Info0::in, Info::out) is det :-
-        Info = Info0 ^ elem(PPId) := {Status, AnalysisStatus}
+        Info = Info0 ^ elem(PPId) :=
+            proc_trailing_info(TrailingStatus, MaybeAnalysisStatus)
     ),
     list.foldl(Update, SCC, TrailingInfo0, TrailingInfo),
     module_info_set_trailing_info(TrailingInfo, !ModuleInfo),
@@ -205,8 +206,14 @@
     globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
     (
         Intermod = yes,
-        record_trailing_analysis_results(Status, AnalysisStatus, SCC,
-            !ModuleInfo)
+        (
+            MaybeAnalysisStatus = yes(AnalysisStatus),
+            record_trailing_analysis_results(TrailingStatus, AnalysisStatus,
+                SCC, !ModuleInfo)
+        ;
+            MaybeAnalysisStatus = no,
+            unexpected(this_file, "process_scc: no analysis status")
+        )
     ;
         Intermod = no
     ),
@@ -229,11 +236,13 @@
     % Examine how the procedures interact with other procedures that
     % are mutually-recursive to them.
     %
-:- func combine_individual_proc_results(proc_results) = trailing_status.
+:- pred combine_individual_proc_results(proc_results::in,
+    trailing_status::out, maybe(analysis_status)::out) is det.
 
-combine_individual_proc_results([]) = _ :-
+combine_individual_proc_results([], _, _) :-
     unexpected(this_file, "Empty SCC during trailing analysis.").
-combine_individual_proc_results(ProcResults @ [_|_]) = SCC_Result :- 
+combine_individual_proc_results(ProcResults @ [_|_],
+        SCC_Result, MaybeAnalysisStatus) :- 
     (
         % If none of the procedures modifies the trail or is conditional then
         % the SCC cannot modify the trail.
@@ -253,14 +262,23 @@
     ;
         % Otherwise the SCC may modify the trail.
         SCC_Result = may_modify_trail
-    ).
+    ),
+    combine_proc_result_maybe_analysis_statuses(ProcResults,
+        MaybeAnalysisStatus).
 
-:- func combine_individual_proc_statuses(proc_results) = analysis_status.
+:- pred combine_proc_result_maybe_analysis_statuses(proc_results::in,
+    maybe(analysis_status)::out) is det.
 
-combine_individual_proc_statuses(ProcResults) =
-    list.foldl((func(proc_result(_, _, Status), Acc)
-            = analysis.lub(Status, Acc)),
-        ProcResults, optimal).
+combine_proc_result_maybe_analysis_statuses(ProcResults,    
+        MaybeAnalysisStatus) :-
+    list.map(maybe_analysis_status, ProcResults, MaybeAnalysisStatuses),
+    list.foldl(combine_maybe_analysis_status, MaybeAnalysisStatuses,
+        yes(optimal), MaybeAnalysisStatus).
+
+:- pred maybe_analysis_status(proc_result::in, maybe(analysis_status)::out)
+    is det.
+
+maybe_analysis_status(ProcResult, ProcResult ^ maybe_analysis_status).
 
 %----------------------------------------------------------------------------%
 %
@@ -275,9 +293,9 @@
     module_info_pred_proc_info(!.ModuleInfo, PPId, _, ProcInfo),
     proc_info_goal(ProcInfo, Body),
     proc_info_vartypes(ProcInfo, VarTypes),
-    check_goal_for_trail_mods(SCC, VarTypes, Body, Result, AnalysisStatus,
-        !ModuleInfo, !IO),
-    list.cons(proc_result(PPId, Result, AnalysisStatus), !Results).
+    check_goal_for_trail_mods(SCC, VarTypes, Body,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO),
+    list.cons(proc_result(PPId, Result, MaybeAnalysisStatus), !Results).
 
 %----------------------------------------------------------------------------%
 %
@@ -285,20 +303,20 @@
 %
 
 :- pred check_goal_for_trail_mods(scc::in, vartypes::in, hlds_goal::in,
-    trailing_status::out, analysis_status::out,
+    trailing_status::out, maybe(analysis_status)::out,
     module_info::in, module_info::out, io::di, io::uo) is det.  
 
 check_goal_for_trail_mods(SCC, VarTypes, Goal - GoalInfo,
-        Result, Status, !ModuleInfo, !IO) :-
+        Result, MaybeStatus, !ModuleInfo, !IO) :-
     check_goal_for_trail_mods_2(SCC, VarTypes, Goal, GoalInfo,
-        Result, Status, !ModuleInfo, !IO).
+        Result, MaybeStatus, !ModuleInfo, !IO).
 
 :- pred check_goal_for_trail_mods_2(scc::in,
-    vartypes::in, hlds_goal_expr::in, hlds_goal_info::in, trailing_status::out, 
-    analysis_status::out, module_info::in, module_info::out, io::di, io::uo)
-    is det.
+    vartypes::in, hlds_goal_expr::in, hlds_goal_info::in,
+    trailing_status::out, maybe(analysis_status)::out,
+    module_info::in, module_info::out, io::di, io::uo) is det.
 
-check_goal_for_trail_mods_2(_, _, Goal, _, will_not_modify_trail, optimal,
+check_goal_for_trail_mods_2(_, _, Goal, _, will_not_modify_trail, yes(optimal),
         !ModuleInfo, !IO) :-
     Goal = unify(_, _, _, Kind, _),
     ( Kind = complicated_unify(_, _, _) ->
@@ -306,8 +324,8 @@
     ;
         true
     ).
-check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _, Result, AnalysisStatus,
-        !ModuleInfo, !IO) :-
+check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     Goal = call(CallPredId, CallProcId, CallArgs, _, _, _),
     CallPPId = proc(CallPredId, CallProcId),    
     module_info_pred_info(!.ModuleInfo, CallPredId, CallPredInfo),
@@ -318,13 +336,13 @@
         Types = list.map((func(Var) = VarTypes ^ det_elem(Var)), CallArgs),
         TrailingStatus = check_types(!.ModuleInfo, Types),
         Result = TrailingStatus,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ; 
         pred_info_is_builtin(CallPredInfo) 
     ->
         % There are no builtins that will modify the trail.
         Result = will_not_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
         % Handle builtin unify and compare.
         % NOTE: the type specific unify and compare predicates are just
@@ -341,81 +359,104 @@
         % XXX We should examine the argument types of calls to builtin.unify/2
         % and builtin.compare/3 and then make a decision based on those.
         Result = may_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
         % Handle library predicates whose trailing status
         % can be looked up in the known procedures table.
         pred_info_has_known_status(CallPredInfo, Result0)
     ->
         Result = Result0,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
-        check_call_2(!.ModuleInfo, VarTypes, CallPPId, CallArgs, MaybeResult),
+        globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
         (
-            MaybeResult = yes({Result, AnalysisStatus})
-        ;
-            MaybeResult = no,
+            Intermod = yes,
+            pred_info_is_imported(CallPredInfo)
+        ->
+            % With --intermodule-analysis use check_call_2 to look up results
+            % for locally defined procedures, otherwise we use the intermodule
+            % analysis framework.
             search_analysis_status(CallPPId, Result, AnalysisStatus, SCC,
-                !ModuleInfo, !IO)
+                !ModuleInfo, !IO),
+            MaybeAnalysisStatus = yes(AnalysisStatus)
+        ;
+            check_call_2(!.ModuleInfo, VarTypes, CallPPId, CallArgs,
+                MaybeResult),
+            (
+                MaybeResult = yes(proc_trailing_info(Result,
+                    MaybeAnalysisStatus))
+            ;
+                MaybeResult = no,
+                % If we do not have any information about the callee procedure
+                % then assume that it modifies the trail.
+                Result = may_modify_trail,
+                (
+                    Intermod = yes,
+                    MaybeAnalysisStatus = yes(suboptimal)
+                ;
+                    Intermod = no,
+                    MaybeAnalysisStatus = no
+                )
+            )
         )
     ).
 
 check_goal_for_trail_mods_2(_, _VarTypes, Goal, _GoalInfo,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     Goal = generic_call(Details, _Args, _ArgModes, _),
     (
         % XXX Use results of closure analysis to handle this.
         Details = higher_order(_Var, _, _,  _),
         Result = may_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
         % XXX We could do better with class methods.
         Details = class_method(_, _, _, _),
         Result = may_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
         Details = cast(_),
         Result  = will_not_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ;
         % XXX I'm not sure what the correct thing to do for 
         % aditi builtins is.
         Details = aditi_builtin(_, _),
         Result = may_modify_trail,
-        AnalysisStatus = optimal
+        MaybeAnalysisStatus = yes(optimal)
     ).
 check_goal_for_trail_mods_2(SCC, VarTypes, not(Goal), _,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
-    check_goal_for_trail_mods(SCC, VarTypes, Goal, Result, AnalysisStatus,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
+    check_goal_for_trail_mods(SCC, VarTypes, Goal, Result, MaybeAnalysisStatus,
         !ModuleInfo, !IO).
 check_goal_for_trail_mods_2(SCC, VarTypes, Goal, OuterGoalInfo,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     Goal = scope(_, InnerGoal),
     check_goal_for_trail_mods(SCC, VarTypes, InnerGoal, Result0,
-        AnalysisStatus, !ModuleInfo, !IO),
+        MaybeAnalysisStatus, !ModuleInfo, !IO),
     InnerGoal = _ - InnerGoalInfo,
     goal_info_get_code_model(InnerGoalInfo, InnerCodeModel),
     goal_info_get_code_model(OuterGoalInfo, OuterCodeModel),
     Result = scope_implies_trail_mod(InnerCodeModel, OuterCodeModel, Result0).
-check_goal_for_trail_mods_2(_, _, Goal, _, Result, AnalysisStatus,
+check_goal_for_trail_mods_2(_, _, Goal, _, Result, MaybeAnalysisStatus,
         !ModuleInfo, !IO) :-
     Goal = foreign_proc(Attributes, _, _, _, _, _),
     Result = attributes_imply_trail_mod(Attributes),
-    AnalysisStatus = optimal.
+    MaybeAnalysisStatus = yes(optimal).
 check_goal_for_trail_mods_2(_, _, shorthand(_), _, _, _, !ModuleInfo, !IO) :-
     unexpected(this_file,
         "shorthand goal encountered during trail usage analysis.").
-check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _, Result, AnalysisStatus,
-        !ModuleInfo, !IO) :-
+check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     Goal = switch(_, _, Cases),
     CaseGoals = list.map((func(case(_, CaseGoal)) = CaseGoal), Cases),
-    check_goals_for_trail_mods(SCC, VarTypes, CaseGoals, Result, AnalysisStatus,
-        !ModuleInfo, !IO).
-check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _, Result, AnalysisStatus,
-        !ModuleInfo, !IO) :-
+    check_goals_for_trail_mods(SCC, VarTypes, CaseGoals,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO).
+check_goal_for_trail_mods_2(SCC, VarTypes, Goal, _,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     Goal = if_then_else(_, If, Then, Else),
     check_goals_for_trail_mods(SCC, VarTypes, [If, Then, Else],
-        Result0, AnalysisStatus, !ModuleInfo, !IO),
+        Result0, MaybeAnalysisStatus, !ModuleInfo, !IO),
     (
         % If none of the disjuncts can modify the trail then we don't need
         % to emit trailing code around this disjunction.
@@ -426,17 +467,17 @@
         Result = may_modify_trail
     ).
 check_goal_for_trail_mods_2(SCC, VarTypes, conj(Goals), _,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
-    check_goals_for_trail_mods(SCC, VarTypes, Goals, Result, AnalysisStatus,
-        !ModuleInfo, !IO).
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
+    check_goals_for_trail_mods(SCC, VarTypes, Goals,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO).
 check_goal_for_trail_mods_2(SCC, VarTypes, par_conj(Goals), _,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
-    check_goals_for_trail_mods(SCC, VarTypes, Goals, Result, AnalysisStatus,
-        !ModuleInfo, !IO).
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
+    check_goals_for_trail_mods(SCC, VarTypes, Goals,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO).
 check_goal_for_trail_mods_2(SCC, VarTypes, disj(Goals), _,
-        Result, AnalysisStatus, !ModuleInfo, !IO) :-
-    check_goals_for_trail_mods(SCC, VarTypes, Goals, Result0, AnalysisStatus,
-        !ModuleInfo, !IO),
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
+    check_goals_for_trail_mods(SCC, VarTypes, Goals,
+        Result0, MaybeAnalysisStatus, !ModuleInfo, !IO),
     (
         % If none of the disjuncts can modify the trail then we don't need
         % to emit trailing code around this disjunction.
@@ -451,16 +492,17 @@
     ).
 
 :- pred check_goals_for_trail_mods(scc::in, vartypes::in,
-    hlds_goals::in, trailing_status::out, analysis_status::out,
+    hlds_goals::in, trailing_status::out, maybe(analysis_status)::out,
     module_info::in, module_info::out, io::di, io::uo) is det.
 
-check_goals_for_trail_mods(SCC, VarTypes, Goals, Result, AnalysisStatus,
-        !ModuleInfo, !IO) :-
+check_goals_for_trail_mods(SCC, VarTypes, Goals,
+        Result, MaybeAnalysisStatus, !ModuleInfo, !IO) :-
     list.map2_foldl2(check_goal_for_trail_mods(SCC, VarTypes), Goals,
-        Results, AnalysisStatuses, !ModuleInfo, !IO),
+        Results, MaybeAnalysisStatuses, !ModuleInfo, !IO),
     list.foldl(combine_trailing_status, Results, will_not_modify_trail,
         Result),
-    AnalysisStatus = list.foldl(analysis.lub, AnalysisStatuses, optimal).
+    list.foldl(combine_maybe_analysis_status, MaybeAnalysisStatuses,
+        yes(optimal), MaybeAnalysisStatus).
 
 %----------------------------------------------------------------------------%
 %
@@ -551,7 +593,7 @@
     list(pred_proc_id)::in, list(pred_proc_id)::out) is semidet.
 
 get_conditional_closure(TrailingInfo, PPId, !Conditionals) :-
-    TrailingInfo ^ elem(PPId) = {Status, _},
+    TrailingInfo ^ elem(PPId) = proc_trailing_info(Status, _),
     (
         Status = conditional,
         list.cons(PPId, !Conditionals)
@@ -570,6 +612,19 @@
 combine_trailing_status(conditional, conditional, conditional).
 combine_trailing_status(conditional, may_modify_trail, may_modify_trail).
 
+:- pred combine_maybe_analysis_status(maybe(analysis_status)::in,
+    maybe(analysis_status)::in, maybe(analysis_status)::out) is det.
+
+combine_maybe_analysis_status(MaybeStatusA, MaybeStatusB, MaybeStatus) :-
+    (
+        MaybeStatusA = yes(StatusA),
+        MaybeStatusB = yes(StatusB)
+    ->
+        MaybeStatus = yes(analysis.lub(StatusA, StatusB))
+    ;
+        MaybeStatus = no
+    ).
+
 %----------------------------------------------------------------------------%
 % 
 % Extra procedures for handling calls
@@ -583,7 +638,7 @@
 check_call(ModuleInfo, VarTypes, PPId, Args, Result) :-
     check_call_2(ModuleInfo, VarTypes, PPId, Args, MaybeResult),
     (
-        MaybeResult = yes({Result, _})
+        MaybeResult = yes(proc_trailing_info(Result, _))
     ;
         MaybeResult = no,
         % If we do not have any information about the callee procedure then
@@ -592,26 +647,28 @@
     ).
 
 :- pred check_call_2(module_info::in, vartypes::in,
-    pred_proc_id::in, prog_vars::in,
-    maybe({trailing_status, analysis_status})::out) is det.
+    pred_proc_id::in, prog_vars::in, maybe(proc_trailing_info)::out) is det.
 
 check_call_2(ModuleInfo, VarTypes, PPId, Args, MaybeResult) :-
     module_info_get_trailing_info(ModuleInfo, TrailingInfo),
-    ( map.search(TrailingInfo, PPId, CalleeTrailingStatus) ->
+    ( map.search(TrailingInfo, PPId, CalleeTrailingInfo) ->
+        CalleeTrailingInfo = proc_trailing_info(CalleeTrailingStatus,
+            AnalysisStatus),
         (
-            CalleeTrailingStatus = {will_not_modify_trail, _},
-            MaybeResult = yes(CalleeTrailingStatus)
+            CalleeTrailingStatus = will_not_modify_trail,
+            MaybeResult = yes(CalleeTrailingInfo)
         ;
-            CalleeTrailingStatus = {may_modify_trail, _},
-            MaybeResult = yes(CalleeTrailingStatus)
+            CalleeTrailingStatus = may_modify_trail,
+            MaybeResult = yes(CalleeTrailingInfo)
         ;
-            CalleeTrailingStatus = {conditional, AnalysisStatus},
+            CalleeTrailingStatus = conditional,
             %
             % This is a call to a polymorphic procedure.  We need to make
             % sure that none of the types involved has a user-defined
             % equality or comparison predicate that modifies the trail.
             % XXX Need to handle higher-order args here as well.
-            MaybeResult = yes({TrailingStatus, AnalysisStatus}),
+            MaybeResult = yes(proc_trailing_info(TrailingStatus,
+                AnalysisStatus)),
             TrailingStatus = check_vars(ModuleInfo, VarTypes, Args)
         )
     ;
@@ -920,6 +977,34 @@
 
 write_pragma_trailing_info(ModuleInfo, TrailingInfo, PredId, !IO) :-
     module_info_pred_info(ModuleInfo, PredId, PredInfo),
+    should_write_trailing_info(ModuleInfo, PredId, PredInfo, ShouldWrite),
+    (   
+        ShouldWrite = yes,
+        ModuleName = pred_info_module(PredInfo),
+        Name       = pred_info_name(PredInfo),
+        Arity      = pred_info_orig_arity(PredInfo),
+        PredOrFunc = pred_info_is_pred_or_func(PredInfo),
+        ProcIds    = pred_info_procids(PredInfo),
+        list.foldl((pred(ProcId::in, !.IO::di, !:IO::uo) is det :-
+            proc_id_to_int(ProcId, ModeNum),
+            ( 
+                map.search(TrailingInfo, proc(PredId, ProcId), ProcTrailInfo),
+                ProcTrailInfo = proc_trailing_info(Status, _)
+            ->
+                mercury_output_pragma_trailing_info(PredOrFunc, 
+                    qualified(ModuleName, Name), Arity,
+                    ModeNum, Status, !IO)
+            ;
+                true
+            )), ProcIds, !IO)
+    ;
+        ShouldWrite = no
+    ).          
+
+:- pred should_write_trailing_info(module_info::in, pred_id::in, 
+        pred_info::in, bool::out) is det.
+
+should_write_trailing_info(ModuleInfo, PredId, PredInfo, ShouldWrite) :-
     pred_info_import_status(PredInfo, ImportStatus),
     (   
         ( ImportStatus = exported 
@@ -938,24 +1023,9 @@
         not check_marker(Markers, class_instance_method),
         not check_marker(Markers, named_class_instance_method)
     ->
-        ModuleName = pred_info_module(PredInfo),
-        Name       = pred_info_name(PredInfo),
-        Arity      = pred_info_orig_arity(PredInfo),
-        PredOrFunc = pred_info_is_pred_or_func(PredInfo),
-        ProcIds    = pred_info_procids(PredInfo),
-        list.foldl((pred(ProcId::in, !.IO::di, !:IO::uo) is det :-
-            proc_id_to_int(ProcId, ModeNum),
-            ( 
-                map.search(TrailingInfo, proc(PredId, ProcId), {Status, _})
-            ->
-                mercury_output_pragma_trailing_info(PredOrFunc, 
-                    qualified(ModuleName, Name), Arity,
-                    ModeNum, Status, !IO)
-            ;
-                true
-            )), ProcIds, !IO)
+        ShouldWrite = yes
     ;
-        true
+        ShouldWrite = no
     ).          
 
 %-----------------------------------------------------------------------------%
@@ -967,13 +1037,12 @@
     --->    trailing_analysis_answer(trailing_status).
 
 :- func analysis_name = string.
-analysis_name = "trailing_info".  % same name as used for the pragmas
+analysis_name = "trail_usage".
 
 :- instance analysis(any_call, trailing_analysis_answer) where [
     analysis_name(_, _) = analysis_name,
     analysis_version_number(_, _) = 1,
     preferred_fixpoint_type(_, _) = least_fixpoint,
-                                    % XXX: I have no idea if this is correct.
     bottom(_) = trailing_analysis_answer(will_not_modify_trail),
     top(_) = trailing_analysis_answer(may_modify_trail)
 ].
@@ -1024,20 +1093,10 @@
 
 search_analysis_status(PPId, Result, AnalysisStatus, CallerSCC,
         !ModuleInfo, !IO) :-
-    globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
-    (
-        Intermod = yes,
-        module_info_get_analysis_info(!.ModuleInfo, AnalysisInfo0),
-        search_analysis_status_2(!.ModuleInfo, PPId, Result, AnalysisStatus,
-            CallerSCC, AnalysisInfo0, AnalysisInfo, !IO),
-        module_info_set_analysis_info(AnalysisInfo, !ModuleInfo)
-    ;
-        Intermod = no,
-        % If we do not have any information about the callee procedure
-        % then assume that it modifies the trail.
-        Result = may_modify_trail,
-        AnalysisStatus = optimal    % unused anyway
-    ).
+    module_info_get_analysis_info(!.ModuleInfo, AnalysisInfo0),
+    search_analysis_status_2(!.ModuleInfo, PPId, Result, AnalysisStatus,
+        CallerSCC, AnalysisInfo0, AnalysisInfo, !IO),
+    module_info_set_analysis_info(AnalysisInfo, !ModuleInfo).
 
 :- pred search_analysis_status_2(module_info::in, pred_proc_id::in,
         trailing_status::out, analysis_status::out, scc::in,
@@ -1045,14 +1104,14 @@
 
 search_analysis_status_2(ModuleInfo, PPId, Result, AnalysisStatus, CallerSCC,
         !AnalysisInfo, !IO) :-
-    module_id_func_id(ModuleInfo, PPId, ModuleId, FuncId),
+    mmc_analysis.module_id_func_id(ModuleInfo, PPId, ModuleId, FuncId),
     Call = any_call,
     analysis.lookup_best_result(ModuleId, FuncId, Call,
         MaybeBestStatus, !AnalysisInfo, !IO),
     (
         MaybeBestStatus = yes({BestCall, trailing_analysis_answer(Result),
             AnalysisStatus}),
-        record_dependencies(analysis_name, ModuleId, FuncId, BestCall,
+        record_dependencies(ModuleId, FuncId, BestCall,
             ModuleInfo, CallerSCC, !AnalysisInfo)
     ;
         MaybeBestStatus = no,
@@ -1062,7 +1121,7 @@
         AnalysisStatus = suboptimal,
         analysis.record_request(analysis_name, ModuleId, FuncId, Call,
             !AnalysisInfo),
-        record_dependencies(analysis_name, ModuleId, FuncId, Call,
+        record_dependencies(ModuleId, FuncId, Call,
             ModuleInfo, CallerSCC, !AnalysisInfo)
     ).
 
@@ -1070,17 +1129,17 @@
     % same module then we don't need to record the dependency so many
     % times, at least while we only have module-level granularity.
     %
-:- pred record_dependencies(analysis_name::in, module_id::in, func_id::in,
-	Call::in, module_info::in, scc::in,
-	analysis_info::in, analysis_info::out) is det
-	<= call_pattern(Call).
+:- pred record_dependencies(module_id::in, func_id::in, Call::in,
+    module_info::in, scc::in, analysis_info::in, analysis_info::out)
+    is det <= call_pattern(Call).
 
-record_dependencies(AnalysisName, ModuleId, FuncId, Call,
+record_dependencies(ModuleId, FuncId, Call,
         ModuleInfo, CallerSCC, !AnalysisInfo) :-
     list.foldl((pred(CallerPPId::in, Info0::in, Info::out) is det :-
-        module_id_func_id(ModuleInfo, CallerPPId, CallerModuleId, _),
+        mmc_analysis.module_id_func_id(ModuleInfo, CallerPPId,
+            CallerModuleId, _),
         analysis.record_dependency(CallerModuleId,
-            AnalysisName, ModuleId, FuncId, Call, Info0, Info)
+            analysis_name, ModuleId, FuncId, Call, Info0, Info)
     ), CallerSCC, !AnalysisInfo).
 
 :- pred record_trailing_analysis_results(trailing_status::in, 
@@ -1098,25 +1157,20 @@
         analysis_status::in, pred_proc_id::in,
         analysis_info::in, analysis_info::out) is det.
 
-record_trailing_analysis_result(ModuleInfo, Status, ResultStatus, PPId,
-        AnalysisInfo0, AnalysisInfo) :-
-    module_id_func_id(ModuleInfo, PPId, ModuleId, FuncId),
-    record_result(ModuleId, FuncId, any_call,
-        trailing_analysis_answer(Status), ResultStatus,
-        AnalysisInfo0, AnalysisInfo).
-
-:- pred module_id_func_id(module_info::in, pred_proc_id::in,
-        module_id::out, func_id::out) is det.
-
-module_id_func_id(ModuleInfo, proc(PredId, ProcId), ModuleId, FuncId) :-
+record_trailing_analysis_result(ModuleInfo, Status, ResultStatus,
+        PPId @ proc(PredId, _ProcId), AnalysisInfo0, AnalysisInfo) :-
     module_info_pred_info(ModuleInfo, PredId, PredInfo),
-    PredModule = pred_info_module(PredInfo),
-    PredName = pred_info_name(PredInfo),
-    PredOrFunc = pred_info_is_pred_or_func(PredInfo),
-    PredArity = pred_info_orig_arity(PredInfo),
-    ModuleId = module_name_to_module_id(PredModule),
-    FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
-        PredName, PredArity, ProcId).
+    should_write_trailing_info(ModuleInfo, PredId, PredInfo, ShouldWrite),
+    (   
+        ShouldWrite = yes,
+        mmc_analysis.module_id_func_id(ModuleInfo, PPId, ModuleId, FuncId),
+        analysis.record_result(ModuleId, FuncId, any_call,
+            trailing_analysis_answer(Status), ResultStatus,
+            AnalysisInfo0, AnalysisInfo)
+    ;
+        ShouldWrite = no,
+        AnalysisInfo = AnalysisInfo0
+    ).
 
 %----------------------------------------------------------------------------%
 %
diff -u compiler/unused_args.m compiler/unused_args.m
--- compiler/unused_args.m	10 Jan 2006 04:35:38 -0000
+++ compiler/unused_args.m	20 Jan 2006 05:00:21 -0000
@@ -164,7 +164,7 @@
     analysis_name(_, _) = analysis_name,
     analysis_version_number(_, _) = 2,
     preferred_fixpoint_type(_, _) = least_fixpoint,
-    bottom(unused_args_call(Arity)) = unused_args(1 `..` Arity),
+    bottom(unused_args_call(Arity)) = unused_args(1 .. Arity),
     top(_) = unused_args([])
 ].
 
@@ -270,6 +270,16 @@
     ;
         MaybeOptFile = no
     ),
+    globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
+    (
+        Intermod = yes,
+        module_info_get_analysis_info(!.ModuleInfo, AnalysisInfo0),
+        list__foldl2(record_intermod_dependencies(!.ModuleInfo),
+            PredProcs, AnalysisInfo0, AnalysisInfo, !IO),
+        module_info_set_analysis_info(AnalysisInfo, !ModuleInfo)
+    ;
+        Intermod = no
+    ),
     globals__io_lookup_bool_option(optimize_unused_args, DoFixup, !IO),
     (
         DoFixup = yes,
@@ -420,13 +430,10 @@
                 ;
                     true
                 ),
-                module_info_get_name(!.ModuleInfo, CallerModule),
-                CallerModuleId = module_name_to_module_id(CallerModule),
-                analysis.record_dependency(CallerModuleId,
-                    analysis_name, PredModuleId, FuncId, Call,
-                    AnalysisInfo1, AnalysisInfo)
+                AnalysisInfo = AnalysisInfo1
             ;
                 MaybeBestResult = no,
+                % XXX makes too many requests
                 analysis.record_request(analysis_name, PredModuleId, 
                     FuncId, Call, AnalysisInfo1, AnalysisInfo)
             ),
@@ -980,8 +987,12 @@
         % XXX: optimal?  If we know some output arguments are not going to be
         % used by the caller then more input arguments could be deduced to be
         % unused.  This analysis doesn't handle that yet.
-        analysis__record_result(ModuleId, FuncId, Call, Answer, optimal,
-            AnalysisInfo1, AnalysisInfo),
+        ( procedure_is_exported(!.ModuleInfo, OrigPredInfo, ProcId) ->
+            analysis__record_result(ModuleId, FuncId, Call, Answer, optimal,
+                AnalysisInfo1, AnalysisInfo)
+        ;
+            AnalysisInfo = AnalysisInfo1
+        ),
         module_info_set_analysis_info(AnalysisInfo, !ModuleInfo)
     ;
         Intermod = no,
@@ -1834,6 +1845,56 @@
 
 %-----------------------------------------------------------------------------%
 
+    % If a procedure in this module calls a procedure from another module,
+    % then we assume that this module depends on the analysis results of that
+    % other procedure.
+    %
+    % This way of constructing the intermodule dependency graph is easier than
+    % actually keeping track of which external analysis results we have used
+    % in order to reach analysis results for this module.
+    % It works because (1) we only have one type of call pattern so we don't
+    % need to know which call patterns are used, and (2) we only record the
+    % entire module as a dependency, so we don't have to know which exported
+    % procedure is calling (directly or indirectly) which imported procedure.
+    %
+:- pred record_intermod_dependencies(module_info::in, pred_proc_id::in,
+    analysis_info::in, analysis_info::out, io::di, io::uo) is det.
+
+record_intermod_dependencies(ModuleInfo, CallerPredProcId,
+        !AnalysisInfo, !IO) :-
+    module_info_pred_proc_info(ModuleInfo, CallerPredProcId,
+        CallerPredInfo, CallerProcInfo),
+    ( not pred_info_is_imported(CallerPredInfo) ->
+        CallerModule = pred_info_module(CallerPredInfo),
+        proc_info_goal(CallerProcInfo, Goal),
+        pred_proc_ids_from_goal(Goal, CalleePredProcIds),
+        list.foldl2(record_intermod_dependencies_2(ModuleInfo, CallerModule),
+            CalleePredProcIds, !AnalysisInfo, !IO)
+    ;
+        true
+    ).
+
+:- pred record_intermod_dependencies_2(module_info::in, module_name::in,
+    pred_proc_id::in, analysis_info::in, analysis_info::out, io::di, io::uo)
+    is det.
+
+record_intermod_dependencies_2(ModuleInfo, CallerModule,
+        CalleePredProcId @ proc(CalleePredId, _), !AnalysisInfo, !IO) :-
+    module_info_pred_info(ModuleInfo, CalleePredId, CalleePredInfo),
+    ( pred_info_is_imported(CalleePredInfo) ->
+        CallerModuleId = module_name_to_module_id(CallerModule),
+        module_id_func_id(ModuleInfo, CalleePredProcId,
+            CalleeModuleId, CalleeFuncId),
+        CalleePredArity = pred_info_orig_arity(CalleePredInfo),
+        Call = unused_args_call(CalleePredArity),
+        analysis.record_dependency(CallerModuleId, analysis_name,
+            CalleeModuleId, CalleeFuncId, Call, !AnalysisInfo)
+    ;
+        true
+    ).
+
+%-----------------------------------------------------------------------------%
+
 :- func this_file = string.
 
 this_file = "unused_args.m".
only in patch2:
unchanged:
--- NEWS	7 Dec 2005 04:57:08 -0000	1.396
+++ NEWS	17 Jan 2006 04:50:51 -0000
@@ -181,7 +181,8 @@
 
 * We have added an `injection' module, for reversible maps that are injective.
 
-* We have added list.foldl_corresponding/5 and list.foldl2_corresponding/7.
+* We have added list.foldl_corresponding/5, list.foldl2_corresponding/7
+  and list.map2_foldl2/8.
 
 * We have added string.word_wrap/2.
 
only in patch2:
unchanged:
--- analysis/README	2 Jan 2003 06:53:53 -0000	1.1
+++ analysis/README	17 Jan 2006 03:50:54 -0000
@@ -18,6 +18,7 @@
 
 TODO:
 - dependency tracking and invalidation after source modifications
+  (partially done but requires testing with more analyses)
 - garbage collection of unused versions
 - least fixpoint analyses
 
@@ -58,14 +59,16 @@
 possibly called predicates is known, but it is better to optimize away
 higher-order or class method calls where possible.
 
+The client should call `analysis__record_dependency' for each external
+analysis result that is made use of by the module.
+
 When compilation of a module is complete, the client should
 call `analysis__write_analysis_files' to write out all
 information collected during the compilation.
 
 Called by analysis passes to record analysis requests and lookup
 answers for imported functions. The status of each answer recorded
-in the database is one of the following (this is currently not
-implemented):
+in the database is one of the following:
 
 * invalid - the answer was computed using information which has changed,
   and must be recomputed. `invalid' entries may not be used in analysis
@@ -81,6 +84,7 @@
   dependency graph which contains `fixpoint_invalid' entries. (Note that
   the method for handling least fixpoint analyses is not described in
   Nicholas Nethercote's thesis).
+  This is not yet implemented.
 
 * suboptimal - the entry does not depend on any `invalid' or
   `fixpoint_invalid' entries, but may be improved by further
only in patch2:
unchanged:
--- compiler/goal_util.m	17 Nov 2005 15:57:14 -0000	1.120
+++ compiler/goal_util.m	20 Jan 2006 05:00:30 -0000
@@ -203,6 +203,10 @@
     %
 :- pred predids_from_goals(list(hlds_goal)::in, list(pred_id)::out) is det.
 
+    % Returns all the procedures that are used within a goal.
+    %
+:- pred pred_proc_ids_from_goal(hlds_goal::in, list(pred_proc_id)::out) is det.
+
 %-----------------------------------------------------------------------------%
 
     % Convert a switch back into a disjunction. This is needed
@@ -1545,6 +1549,10 @@
     P = (pred(PredId::out) is nondet :- goal_calls_pred_id(Goal, PredId)),
     solutions(P, PredIds).
 
+pred_proc_ids_from_goal(Goal, PredProcIds) :-
+    P = (pred(PredProcId::out) is nondet :- goal_calls(Goal, PredProcId)),
+    solutions(P, PredProcIds).
+
 %-----------------------------------------------------------------------------%
 
 foreign_code_uses_variable(Impl, VarName) :-
only in patch2:
unchanged:
--- compiler/make.dependencies.m	17 Nov 2005 15:57:22 -0000	1.21
+++ compiler/make.dependencies.m	17 Jan 2006 03:42:26 -0000
@@ -212,6 +212,14 @@
             long_interface `of` non_intermod_direct_imports,
             short_interface `of` non_intermod_indirect_imports
         ]).
+target_dependencies(_, analysis_registry) = 
+    % XXX not sure about this.
+    combine_deps_list([
+        source `of` self,
+        private_interface `of` parents,
+        long_interface `of` non_intermod_direct_imports,
+        short_interface `of` non_intermod_indirect_imports
+    ]).
 target_dependencies(_, foreign_il_asm(_)) =
     combine_deps_list([
         il_asm `of` self,
only in patch2:
unchanged:
--- compiler/make.m	11 Jan 2006 02:33:40 -0000	1.31
+++ compiler/make.m	17 Jan 2006 03:04:01 -0000
@@ -171,6 +171,7 @@
     ;       make_private_interface
     ;       make_optimization_interface
     ;       make_transitive_optimization_interface
+    ;       make_analysis_registry
     ;       compile_to_target_code.
 
 :- type module_target_type
@@ -181,6 +182,7 @@
     ;       short_interface
     ;       unqualified_short_interface
     ;       intermodule_interface
+    ;       analysis_registry
     ;       aditi_code
     ;       c_header(c_header_type)
     ;       c_code
only in patch2:
unchanged:
--- compiler/make.module_target.m	28 Nov 2005 04:11:45 -0000	1.34
+++ compiler/make.module_target.m	19 Jan 2006 05:28:58 -0000
@@ -622,6 +622,8 @@
 compilation_task(_, intermodule_interface) =
     process_module(make_optimization_interface) -
         ["--make-optimization-interface"].
+compilation_task(_, analysis_registry) =
+    sorry(this_file, ".analysis targets").
 compilation_task(_, aditi_code) =
     process_module(compile_to_target_code) - ["--aditi-only"].
 compilation_task(Globals, c_header(_)) = compilation_task(Globals, c_code).
only in patch2:
unchanged:
--- compiler/make.program_target.m	6 Jan 2006 04:06:51 -0000	1.36
+++ compiler/make.program_target.m	17 Jan 2006 03:09:23 -0000
@@ -1115,11 +1115,13 @@
     make_module_clean(ModuleName, !Info, !IO),
     list__foldl2(remove_target_file(ModuleName),
         [private_interface, long_interface, short_interface,
-        unqualified_short_interface, intermodule_interface,
+        unqualified_short_interface, intermodule_interface, analysis_registry,
         aditi_code, c_header(mh)
         ],
         !Info, !IO),
-    remove_file(ModuleName, module_dep_file_extension, !Info, !IO).
+    remove_file(ModuleName, module_dep_file_extension, !Info, !IO),
+    remove_file(ModuleName, ".imdg", !Info, !IO),
+    remove_file(ModuleName, ".request", !Info, !IO).
 
 %-----------------------------------------------------------------------------%
 
only in patch2:
unchanged:
--- compiler/make.util.m	13 Dec 2005 00:25:04 -0000	1.28
+++ compiler/make.util.m	17 Jan 2006 03:05:39 -0000
@@ -730,6 +730,7 @@
 target_extension(_, short_interface) = yes(".int2").
 target_extension(_, unqualified_short_interface) = yes(".int3").
 target_extension(_, intermodule_interface) = yes(".opt").
+target_extension(_, analysis_registry) = yes(".analysis").
 target_extension(_, aditi_code) = yes(".rlo").
 target_extension(_, c_header(mih)) = yes(".mih").
 target_extension(_, c_header(mh)) = yes(".mh").
@@ -851,6 +852,7 @@
 search_for_file_type(short_interface) = yes(search_directories).
 search_for_file_type(unqualified_short_interface) = yes(search_directories).
 search_for_file_type(intermodule_interface) = yes(intermod_directories).
+search_for_file_type(analysis_registry) = yes(intermod_directories).
 search_for_file_type(aditi_code) = no.
 search_for_file_type(c_header(_)) = yes(c_include_directory).
 search_for_file_type(c_code) = no.
@@ -876,6 +878,7 @@
 target_is_grade_or_arch_dependent(short_interface, no).
 target_is_grade_or_arch_dependent(unqualified_short_interface, no).
 target_is_grade_or_arch_dependent(intermodule_interface, yes).
+target_is_grade_or_arch_dependent(analysis_registry, yes).
 target_is_grade_or_arch_dependent(aditi_code, no).
 target_is_grade_or_arch_dependent(c_header(mh), no).
 target_is_grade_or_arch_dependent(c_header(mih), yes).
only in patch2:
unchanged:
--- compiler/modules.m	6 Dec 2005 01:56:29 -0000	1.368
+++ compiler/modules.m	17 Jan 2006 03:28:21 -0000
@@ -4969,6 +4969,12 @@
     io__write_string(DepStream, "\n", !IO),
 
     io__write_string(DepStream, MakeVarName, !IO),
+    io__write_string(DepStream, ".imdgs = ", !IO),
+    write_compact_dependencies_list(Modules, "$(imdgs_subdir)",
+        ".imdg", Basis, DepStream, !IO),
+    io__write_string(DepStream, "\n", !IO),
+
+    io__write_string(DepStream, MakeVarName, !IO),
     io__write_string(DepStream, ".schemas = ", !IO),
     write_compact_dependencies_list(Modules, "", ".base_schema",
         Basis, DepStream, !IO),
@@ -5548,6 +5554,7 @@
         "\t-echo $(", MakeVarName, ".trans_opts) | xargs rm -f\n",
         "\t-echo $(", MakeVarName, ".analysiss) | xargs rm -f\n",
         "\t-echo $(", MakeVarName, ".requests) | xargs rm -f\n",
+        "\t-echo $(", MakeVarName, ".imdgs) | xargs rm -f\n",
         "\t-echo $(", MakeVarName, ".ds) | xargs rm -f\n",
         "\t-echo $(", MakeVarName, ".module_deps) | xargs rm -f\n",
         "\t-echo $(", MakeVarName, ".all_mhs) | xargs rm -f\n",
only in patch2:
unchanged:
--- scripts/Mmake.vars.in	28 Nov 2005 02:30:31 -0000	1.99
+++ scripts/Mmake.vars.in	20 Jan 2006 00:35:55 -0000
@@ -594,6 +594,7 @@
 trans_opts_subdir=$(SUBDIR)trans_opts/
 analysiss_subdir=$(SUBDIR)analysiss/
 requests_subdir=$(SUBDIR)requests/
+imdgs_subdir=$(SUBDIR)imdgs/
 date0s_subdir=$(SUBDIR)date0s/
 dates_subdir=$(SUBDIR)dates/
 date3s_subdir=$(SUBDIR)date3s/
@@ -635,6 +636,7 @@
 trans_opts_subdir=
 analysiss_subdir=
 requests_subdir=
+imdgs_subdir=
 date0s_subdir=
 dates_subdir=
 date3s_subdir=
--------------------------------------------------------------------------
mercury-reviews mailing list
post:  mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the reviews mailing list