[m-rev.] for review: inter-module analysis framework

Simon Taylor stayl at cs.mu.OZ.AU
Tue Aug 27 18:54:14 AEST 2002


On 27-Aug-2002, Fergus Henderson <fjh at cs.mu.OZ.AU> wrote:
> Could you please post an updated full diff?

Estimated hours taken: 80
Branches: main

A first implementation of the inter-module analysis framwork.
Currently only unused argument analysis is supported.

The current inter-module analysis scheme using `.trans_opt' files
has some major limitations. The compilation dependencies introduced
by `.trans_opt' files are too complicated for Mmake without major
limitations on which modules can use the contents of which `.trans_opt'
files. Also, the `.trans_opt' file system only computes greatest fixpoints, 
which is often too weak to find opportunities for optimization.
A better solution is to provide a library which manually handles
the dependencies introduced by inter-module analysis, and can deal with 
the complications introduced by cyclic module dependencies.

TODO:
- support other analyses, e.g. termination, type specialization
- dependency tracking and invalidation after source modifications
- garbage collection of unused versions
- least fixpoint analyses

analysis/Mmakefile:
analysis/mer_analysis.m:
analysis/analysis.m:
analysis/analysis.file.m:
	The analysis library.

analysis/README:
	Description and design documentation.

Mmake.workspace:
Mmakefile:
compiler/Mmakefile:
tools/bootcheck:
	Link the analysis library into mercury_compile.

compiler/hlds_module.m:
	Store analysis information in the module_info.

compiler/options.m:
doc/user_guide.texi:
	Add an option `--intermodule-analysis'.

compiler/mercury_compile.m:
	Call the analysis library to write the gathered
	information at the end of a compilation.

compiler/unused_args.m:
	Call the analysis library to retrieve information
	about imported procedures. This replaces code which
	used the `.opt' files.

	Change the names created for unused arguments procedures
	to include the arguments removed, rather than a sequence
	number. I think Zoltan is working on a change to name
	mangling, so I haven't updated the demangler.

compiler/prog_util.m:
	Generate the new predicate names for unused_args.m.

library/std_util.m:
	Add a polymorphic version of unit, which is useful
	for binding type variables.

compiler/modules.m:
scripts/Mmake.vars.in:
	Clean up files created by the analysis framework
	in `mmake realclean'.

util/mdemangle.c:
profiler/demangle.m:
	Document the change in the name mangling of procedures with
	unused arguments.

configure.in:
	Check for state variables and fixes for some typeclass bugs.

tests/warnings/Mmakefile:
tests/warnings/unused_args_analysis.{m,exp}:
	Test case.

Index: Mmake.workspace
===================================================================
RCS file: /home/mercury1/repository/mercury/Mmake.workspace,v
retrieving revision 1.9
diff -u -u -r1.9 Mmake.workspace
--- Mmake.workspace	21 Aug 2002 11:27:06 -0000	1.9
+++ Mmake.workspace	22 Aug 2002 07:57:10 -0000
@@ -54,6 +54,7 @@
 MPS_GC_DIR = $(WORKSPACE)/mps_gc/code
 COMPILER_DIR = $(WORKSPACE)/compiler
 UTIL_DIR = $(WORKSPACE)/util
+ANALYSIS_DIR = $(WORKSPACE)/analysis
 
 # Specify the MPS "platform"
 # E.g. lii4gc means Linux, i486, gcc
@@ -79,6 +80,7 @@
 STD_LIB_NAME = mer_std
 TRACE_LIB_NAME = mer_trace
 BROWSER_LIB_NAME = mer_browser
+ANALYSIS_LIB_NAME = mer_analysis
 
 # This specifies the path to the so_locations file (or its equivalent),
 # which is used by the linker to help it to map different shared objects
Index: Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/Mmakefile,v
retrieving revision 1.83
diff -u -u -r1.83 Mmakefile
--- Mmakefile	22 Aug 2002 02:34:07 -0000	1.83
+++ Mmakefile	22 Aug 2002 07:57:11 -0000
@@ -30,6 +30,7 @@
 		library \
 		trace \
 		browser \
+		analysis \
 		compiler \
 		doc \
 		profiler \
@@ -53,7 +54,8 @@
 # `mmake depend' forces them to be remade to ensure that they are up-to-date.
 
 .PHONY: dep
-dep: dep_library dep_browser dep_compiler dep_profiler dep_deep_profiler
+dep: dep_library dep_browser dep_analysis dep_compiler \
+		dep_profiler dep_deep_profiler
 
 .PHONY: dep_library
 dep_library: library/$(deps_subdir)mer_std.dep
@@ -67,6 +69,12 @@
 browser/$(deps_subdir)mer_browser.dep:
 	+cd browser && $(SUBDIR_MMAKE) depend
 
+.PHONY: dep_analysis
+dep_analysis: analysis/$(deps_subdir)mer_analysis.dep
+
+analysis/$(deps_subdir)mer_analysis.dep:
+	+cd analysis && $(SUBDIR_MMAKE) depend
+
 .PHONY: dep_compiler
 dep_compiler: compiler/$(deps_subdir)top_level.dep
 
@@ -99,6 +107,7 @@
 depend:
 	+cd library && $(SUBDIR_MMAKE) depend
 	+cd browser && $(SUBDIR_MMAKE) depend
+	+cd analysis && $(SUBDIR_MMAKE) depend
 	+cd compiler && $(SUBDIR_MMAKE) depend
 	+cd profiler && $(SUBDIR_MMAKE) depend
 	+cd deep_profiler && $(SUBDIR_MMAKE) depend
@@ -153,16 +162,22 @@
 browser: dep_browser scripts util boehm_gc runtime library
 	+cd browser && $(SUBDIR_MMAKE)
 
+.PHONY: analysis
+analysis: dep_analysis scripts util boehm_gc runtime library browser trace
+	+cd analysis && $(SUBDIR_MMAKE)
+
 .PHONY: runtime
 trace: scripts boehm_gc runtime library browser
 	+cd trace && $(SUBDIR_MMAKE)
 
 .PHONY: compiler
-compiler: dep_compiler scripts util boehm_gc runtime library browser trace
+compiler: dep_compiler scripts util boehm_gc runtime library \
+		browser trace analysis
 	+cd compiler && $(SUBDIR_MMAKE)
 
 .PHONY: libmmc
-libmmc: dep_compiler scripts util boehm_gc runtime library browser trace
+libmmc: dep_compiler scripts util boehm_gc runtime library \
+		browser trace analysis
 	+cd compiler && $(SUBDIR_MMAKE) libmmc
 
 .PHONY: doc
@@ -181,7 +196,8 @@
 #-----------------------------------------------------------------------------#
 
 .PHONY: tags
-tags: tags_compiler tags_library tags_browser tags_profiler tags_deep_profiler
+tags: tags_compiler tags_library tags_browser tags_analysis \
+		tags_profiler tags_deep_profiler
 
 .PHONY: tags_compiler
 tags_compiler:
@@ -195,6 +211,10 @@
 tags_browser:
 	+cd browser && $(SUBDIR_MMAKE) tags
 
+.PHONY: tags_analysis
+tags_analysis:
+	+cd analysis && $(SUBDIR_MMAKE) tags
+
 .PHONY: tags_profiler
 tags_profiler:
 	+cd profiler && $(SUBDIR_MMAKE) tags
@@ -208,7 +228,8 @@
 # Remove from each of the listed directories mmc-generated files that don't
 # belong there.
 cleanint:
-	for dir in browser compiler deep_profiler library profiler; do \
+	for dir in analysis browser compiler deep_profiler library profiler; \
+	do \
 		echo Looking for inappropriate files in the $$dir directory: ; \
 		( cd $$dir && ../tools/cleanint > .cleanint ) ; \
 		if test -s $$dir/.cleanint ; then \
@@ -261,6 +282,8 @@
 	+cd browser && $(SUBDIR_MMAKE) depend
 	+cd browser && $(SUBDIR_MMAKE) all-ints cs $(BROWSER_LIB_NAME).init tags
 	+cd trace && $(SUBDIR_MMAKE) cs
+	+cd analysis && $(SUBDIR_MMAKE) depend
+	+cd analysis && $(SUBDIR_MMAKE) cs tags
 	+cd compiler && $(SUBDIR_MMAKE) depend
 	+cd compiler && $(SUBDIR_MMAKE) cs tags
 	+cd profiler && $(SUBDIR_MMAKE) depend
@@ -515,6 +538,9 @@
 	touch browser/*.optdate
 	chmod +w browser/*.dep
 	touch browser/*.dep
+	touch analysis/*.date*
+	chmod +w analysis/*.dep
+	touch analysis/*.dep
 	touch compiler/*.date*
 	chmod +w compiler/*.dep
 	touch compiler/*.dep
Index: configure.in
===================================================================
RCS file: /home/mercury1/repository/mercury/configure.in,v
retrieving revision 1.314
diff -u -u -r1.314 configure.in
--- configure.in	7 Aug 2002 23:36:49 -0000	1.314
+++ configure.in	8 Aug 2002 16:13:23 -0000
@@ -104,12 +104,12 @@
 
 		:- import_module int.
         
-		main --> 
-			{ return_rtti_version(Version) },
-			( { Version >= 6 } ->
-				print("Hello, world\n")
+		main(!IO) :-
+			return_rtti_version(Version),
+			( Version >= 6 ->
+				print("Hello, world\n", !IO)
 			;
-				print("Nope.\n")
+				print("Nope.\n", !IO)
 			).
 
 		:- pred return_rtti_version(int::out) is det.
@@ -123,7 +123,7 @@
 		# Test for the `--bug-intermod-2002-06-13' option.
 		echo $BOOTSTRAP_MC conftest >&AC_FD_CC 2>&1 &&
 		$BOOTSTRAP_MC \
-			--bug-intermod-2002-06-13 \
+			--bug-foreign_import-2002-08-06 \
 			--halt-at-warn $link_static_opt conftest \
 			</dev/null >&AC_FD_CC 2>&1 &&
 		test "`./conftest 2>&1 | tr -d '\015'`" = "Hello, world"
Index: analysis/Mmakefile
===================================================================
RCS file: analysis/Mmakefile
diff -N analysis/Mmakefile
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ analysis/Mmakefile	2 Aug 2002 05:43:57 -0000
@@ -0,0 +1,113 @@
+#-----------------------------------------------------------------------------#
+# Copyright (C) 2002 The University of Melbourne.
+# This file may only be copied under the terms of the GNU General
+# Public License - see the file COPYING in the Mercury distribution.
+#-----------------------------------------------------------------------------#
+# analysis/Mmakefile - this is the Mmakefile for building the Mercury
+# inter-module analysis library.
+
+MERCURY_DIR=..
+LINK_STDLIB_ONLY=yes
+include $(MERCURY_DIR)/Mmake.common
+
+# Module-specific options should go in Mercury.options so they
+# can be found by `mmc --make'.
+include Mercury.options
+
+MAIN_TARGET=library
+MERCURY_MAIN_MODULES=$(ANALYSIS_LIB_NAME)
+
+#-----------------------------------------------------------------------------#
+
+# Specify which compilers to use to compile the library.
+# Don't change these without good reason - if you want to
+# do a temporary change, change ../Mmake.params.
+
+MLFLAGS +=	-R$(FINAL_INSTALL_MERC_LIB_DIR)	\
+		-R$(FINAL_INSTALL_MERC_GC_LIB_DIR)
+
+MTAGS	=	$(SCRIPTS_DIR)/mtags
+
+LN	=	ln
+
+#-----------------------------------------------------------------------------#
+
+# Stuff for Windows DLLS using gnu-win32
+
+ifeq ($(USE_DLLS),yes)
+
+DLL_CFLAGS = -Dlib$(ANALYSIS_LIB_NAME)_DEFINE_DLL
+
+include $(MERCURY_DIR)/Makefile.DLLs
+
+else
+
+DLL_CFLAGS =
+DLL_DEF_LIB =
+
+endif
+
+#-----------------------------------------------------------------------------#
+
+# targets
+
+.PHONY: all
+all : library
+
+DEPENDS = $(ANALYSIS_LIB_NAME).depend
+.PHONY: depend
+depend		: $(DEPENDS)
+
+.PHONY: check
+check		: $(ANALYSIS_LIB_NAME).check
+
+.PHONY: all-ints 
+all-ints: ints int3s
+
+.PHONY: ints 
+ints		: $(ANALYSIS_LIB_NAME).ints
+
+.PHONY: int3s 
+int3s		: $(ANALYSIS_LIB_NAME).int3s
+
+#-----------------------------------------------------------------------------#
+
+tags		: $(MTAGS) $($(ANALYSIS_LIB_NAME).ms)
+	$(MTAGS) $($(ANALYSIS_LIB_NAME).ms) ../library/*.m
+
+$(ANALYSIS_LIB_NAME).stats : $(COMPILER_DIR)/source_stats.awk \
+				$($(ANALYSIS_LIB_NAME).ms)
+	awk -f $(COMPILER_DIR)/source_stats.awk \
+		`vpath_find $($(ANALYSIS_LIB_NAME).ms)` > $@
+	
+#-----------------------------------------------------------------------------#
+
+.PHONY: dates
+dates		:
+	touch $($(ANALYSIS_LIB_NAME).dates)
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: os cs ss
+ifeq ($(MMAKE_USE_MMC_MAKE),no)
+os: $($(ANALYSIS_LIB_NAME).os)
+cs: $($(ANALYSIS_LIB_NAME).cs)
+ss: $($(ANALYSIS_LIB_NAME).ss)
+else
+os: $(ANALYSIS_LIB_NAME).os
+cs: $(ANALYSIS_LIB_NAME).cs
+ss: $(ANALYSIS_LIB_NAME).ss
+endif
+
+#-----------------------------------------------------------------------------#
+
+.PHONY: library
+library: lib$(ANALYSIS_LIB_NAME)
+
+#-----------------------------------------------------------------------------#
+
+# Installation targets
+
+.PHONY: install
+install:
+
Index: analysis/README
===================================================================
RCS file: analysis/README
diff -N analysis/README
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ analysis/README	27 Aug 2002 06:00:07 -0000
@@ -0,0 +1,157 @@
+
+This directory contains an implementation of the inter-module
+analysis framework described in 
+
+	Nicholas Nethercote. The Analysis Framework of HAL,
+	Chapter 7: Inter-module Analysis, Master's Thesis,
+	University of Melbourne, September 2001, revised April 2002.
+	<http://www.cl.cam.ac.uk/~njn25/pubs/masters2001.ps.gz>.
+
+This framework records call and answer patterns for arbitrary analyses,
+and performs dependency analysis to force recompilation where necessary
+when modules change.
+
+TODO:
+- dependency tracking and invalidation after source modifications
+- garbage collection of unused versions
+- least fixpoint analyses
+
+DESIGN:
+
+The analysis framework is a library which links into the client
+compiler, allowing the class methods to examine compiler data
+structures. The interface is as compiler-independent as possible,
+so that compilers which can interface with Mercury code via .NET
+can use it.
+
+Clients of the library must define an instance of the typeclass
+`analysis__compiler', which describes the analyses the compiler
+wants to perform.
+
+Each analysis is described by a call pattern type and an
+answer pattern type. A call pattern describes the information
+known about the argument variables before analysing a call
+(by executing it in the abstract domain used by the analysis).
+An answer pattern describes the information known after analysing
+the call. Call and answer patterns must form a partial order, and
+must be convertible to strings.
+
+Analysis database
+=================
+
+When analysing a module, at each call to an imported function
+the client should call `analysis__lookup_results' or
+`analysis__lookup_best_result' to find the results which
+match the call pattern.
+
+If no results exist, the client should call `analysis__record_request',
+to ask that a specialized version be created on the next compilation
+of the client module.
+
+There is currently no way to analyse higher-order or class method
+calls. It might be possible to analyse such calls where the set of
+possibly called predicates is known, but it is better to optimize away
+higher-order or class method calls where possible.
+
+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):
+
+* invalid - the answer was computed using information which has changed,
+  and must be recomputed. `invalid' entries may not be used in analysis
+  or in generating code.
+
+* fixpoint_invalid - the entry is for a least fixpoint analysis, and
+  depends on an answer which has changed so that the new answer
+  is strictly less precise than the old answer (moving towards to
+  correct answer). `fixpoint_invalid' entries may be used when analysing
+  a module, but code must not be generated which uses `fixpoint_invalid'
+  results (even indirectly). In addition, code must not be generated when
+  compiling a module in a strongly connected component of the analysis
+  dependency graph which contains `fixpoint_invalid' entries. (Note that
+  the method for handling least fixpoint analyses is not described in
+  Nicholas Nethercote's thesis).
+
+* suboptimal - the entry does not depend on any `invalid' or
+  `fixpoint_invalid' entries, but may be improved by further
+  recompilation. `suboptimal' entries do not need to be recompiled,
+  but efficiency may be improved if they are. `suboptimal' annotations
+  are only possible for greatest fixpoint analyses (least fixpoint
+  analyses start with a "super-optimal" answer and work towards the
+  correct answer).
+
+* optimal - the entry does not depend on any `invalid', `fixpoint_invalid'
+  or `suboptimal' results. Modules containing only `optimal' entries do
+  not need recompilation.
+
+Analysis dependency checker (NYI)
+=================================
+
+Examines the dependencies between analysis results and the state
+of the compilation, then orders recompilations so that there are no
+`invalid' or `fixpoint_invalid' entries (with an option to eliminate
+`suboptimal' entries).
+
+Each client compiler should have an option which invokes the analysis
+dependency checker rather than compiling code. This adjusts the status
+of entries in the database, then invokes the compiler's build tools
+(through a typeclass method) to recompile modules in the correct order.
+
+If the implementation of a function changes, all of its answers are
+marked as invalid, and the results of the functions it directly uses
+in the SCC of the analysis dependency graph containing it are reset
+to `top' (marked `suboptimal') for greatest fixpoint analyses, or
+`bottom' (marked `fixpoint_invalid') for least fixpoint analyses.
+This ensures that the new result for the function is not computed
+using potentially invalid information.
+
+After each compilation, the dependency checker examines the changes
+in the analysis results for each function.
+
+For greatest fixpoint analyses, if the new answer is
+- less precise than or incomparable with the old result,
+  all users of the call pattern are marked `invalid'.
+- equal to the old result, no entries need to be marked. 
+- more precise than the old result, callers are marked
+  as `suboptimal'.
+
+For least fixpoint analyses, if the new answer is
+- less precise than or incomparable with the old result,
+  all users of the call pattern are marked `invalid'.
+- equal to the old result, no entries need to be marked. 
+- more precise than the old result, callers are marked
+  as `fixpoint_invalid'.
+
+The new answer itself will be marked as `optimal'. This isn't
+necessarily correct -- further recompilations may change its status
+to `fixpoint_invalid' or `suboptimal' (or `invalid' if there
+are source code changes).
+
+Recompilation must proceed until there are no `invalid' or `fixpoint_invalid'
+entries. Optionally, optimization can proceed until there are no new requests
+or `suboptimal' answers.
+
+It the responsibility of the analysis implementor to ensure termination of
+the analysis process by not generating an infinite number of requests.
+
+Granularity of dependencies
+===========================
+
+The description in Nicholas Nethercote's thesis uses fine-grained
+dependency tracking, where for each exported answer only the imported
+analysis results used to compute that answer are recorded.
+
+For simplicity, the initial Mercury implementation will only record
+dependencies of entire modules on particular analysis results 
+(effectively the exported results depend on all imported analysis
+results used in that compilation). This is worthwhile because none of
+the analyses in the Mercury compiler currently record the information
+required for the more precise approach, and I would expect that other
+compilers not designed for inter-module analysis would also not
+record that information.
+
Index: analysis/analysis.file.m
===================================================================
RCS file: analysis/analysis.file.m
diff -N analysis/analysis.file.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ analysis/analysis.file.m	26 Aug 2002 15:26:28 -0000
@@ -0,0 +1,370 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2002 University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+% File: analysis.file.m
+% Main author: stayl
+%
+% An analysis file contains analysis results for a single module.
+%-----------------------------------------------------------------------------%
+:- module analysis__file.
+
+:- interface.
+
+:- pred read_module_analysis_results(analysis_info::in,
+	module_id::in, module_analysis_map(analysis_result)::out,
+	io__state::di, io__state::uo) is det.
+
+:- pred write_module_analysis_results(analysis_info::in,
+	module_id::in, module_analysis_map(analysis_result)::in,
+	io__state::di, io__state::uo) is det.
+
+:- pred read_module_analysis_requests(analysis_info::in,
+	module_id::in, module_analysis_map(analysis_request)::out,
+	io__state::di, io__state::uo) is det.
+
+:- pred write_module_analysis_requests(analysis_info::in,
+	module_id::in, module_analysis_map(analysis_request)::in,
+	io__state::di, io__state::uo) is det.
+
+:- pred empty_request_file(analysis_info::in, module_id::in,
+	io__state::di, io__state::uo) is det.
+
+%-----------------------------------------------------------------------------%
+:- implementation.
+% The format of an analysis file is:
+%
+% version_number.
+% analysis_name(analysis_version, func_id, call_pattern, answer_pattern).
+%-----------------------------------------------------------------------------%
+:- import_module bool, exception, parser, term, term_io, varset.
+
+:- type invalid_analysis_file ---> invalid_analysis_file.
+
+:- func version_number = int.
+version_number = 1.
+
+read_module_analysis_results(Info, ModuleId, ModuleResults, !IO) :-
+	read_analysis_file(Info ^ compiler, ModuleId, ".analysis",
+		parse_result_entry(Info ^ compiler),
+		map__init, ModuleResults, !IO).
+
+:- pred parse_result_entry(Compiler::in)
+		`with_type` parse_entry(module_analysis_map(analysis_result))
+		`with_inst` parse_entry <= compiler(Compiler).
+
+parse_result_entry(Compiler, Term, Results0, Results) :-
+    (	
+	Term = term__functor(term__atom(AnalysisName),
+			[VersionNumberTerm, FuncIdTerm,
+			CallPatternTerm, AnswerPatternTerm], _),
+	FuncIdTerm = term__functor(term__string(FuncId), [], _),
+	CallPatternTerm = term__functor(
+			term__string(CallPatternString), [], _),
+	AnswerPatternTerm = term__functor(
+			term__string(AnswerPatternString), [], _),
+	analysis_type(_ `with_type` unit(FuncInfo), _ `with_type` unit(Call),
+			_ `with_type` unit(Answer)) =
+			analyses(Compiler, AnalysisName),
+
+	CallPattern = from_string(CallPatternString) `with_type` Call,
+	AnswerPattern = from_string(AnswerPatternString) `with_type` Answer
+    ->
+	(
+		VersionNumber = analysis_version_number(
+				_ `with_type` FuncInfo, _ `with_type` Call,
+				_ `with_type` Answer),
+		VersionNumberTerm = term__functor(
+				term__integer(VersionNumber), [], _)
+	->
+		Result = 'new analysis_result'(
+				unit1 `with_type` unit(FuncInfo),
+				CallPattern, AnswerPattern),
+		( AnalysisResults0 = map__search(Results0, AnalysisName) ->
+			AnalysisResults1 = AnalysisResults0
+		;
+			AnalysisResults1 = map__init
+		),
+		(
+			FuncResults0 = map__search(AnalysisResults1,
+				FuncId)
+		->
+			FuncResults = [Result | FuncResults0]
+		;
+			FuncResults = [Result]
+		),
+		Results = map__set(Results0, AnalysisName, 
+			map__set(AnalysisResults1,
+				FuncId, FuncResults))
+    	;
+		% Ignore results with an out-of-date version number.
+		Results = Results0
+	)
+    ;
+	throw(invalid_analysis_file)
+    ).
+
+read_module_analysis_requests(Info, ModuleId, ModuleRequests, !IO) :-
+	read_analysis_file(Info ^ compiler, ModuleId, ".request",
+		parse_request_entry(Info ^ compiler),
+		map__init, ModuleRequests, !IO).
+
+:- pred parse_request_entry(Compiler::in)
+		`with_type` parse_entry(module_analysis_map(analysis_request))
+		`with_inst` parse_entry <= compiler(Compiler).
+
+parse_request_entry(Compiler, Term, Requests0, Requests) :-
+    (	
+	Term = term__functor(term__atom(AnalysisName),
+			[VersionNumberTerm, FuncIdTerm, CallPatternTerm], _),
+	FuncIdTerm = term__functor(term__string(FuncId), [], _),
+	CallPatternTerm = term__functor(
+		term__string(CallPatternString), [], _),
+	analysis_type(_ `with_type` unit(FuncInfo),
+		_ `with_type` unit(Call), _ `with_type` unit(Answer)) =
+		analyses(Compiler, AnalysisName),
+	CallPattern = from_string(CallPatternString) `with_type` Call
+    ->
+	(
+		VersionNumber = analysis_version_number(
+				_ `with_type` FuncInfo, _ `with_type` Call,
+				_ `with_type` Answer),
+		VersionNumberTerm = term__functor(
+				term__integer(VersionNumber), [], _)
+	->
+		Result = 'new analysis_request'(
+				unit1 `with_type` unit(FuncInfo),
+				CallPattern),
+		(
+			AnalysisRequests0 = map__search(Requests0,
+				AnalysisName)
+		->
+			AnalysisRequests1 = AnalysisRequests0
+		;
+			AnalysisRequests1 = map__init
+		),
+		(
+			FuncRequests0 = map__search(AnalysisRequests1,
+				FuncId)
+		->
+			FuncRequests = [Result | FuncRequests0]
+		;
+			FuncRequests = [Result]
+		),
+		Requests = map__set(Requests0, AnalysisName, 
+			map__set(AnalysisRequests1,
+				FuncId, FuncRequests))
+    	;
+		% Ignore requests with an out-of-date version number.
+		Requests = Requests0
+	)
+    ;
+	throw(invalid_analysis_file)
+    ).
+
+:- 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,
+		parse_entry(T)::in(parse_entry), T::in, T::out,
+		io__state::di, io__state::uo) is det <= compiler(Compiler).
+
+read_analysis_file(Compiler, ModuleId, Suffix, ParseEntry,
+		ModuleResults0, ModuleResults, !IO) :-
+	module_id_to_file_name(Compiler, ModuleId,
+		Suffix, AnalysisFileName, !IO),
+	io__open_input(AnalysisFileName, OpenResult, !IO),
+	(
+		OpenResult = ok(Stream),
+		io__set_input_stream(Stream, OldStream, !IO),
+		promise_only_solution_io(
+		    (pred(R::out, di, uo) is cc_multi -->
+			try_io((pred(Results1::out, di, uo) is det -->
+			    read_analysis_file_2(ParseEntry,
+			    		ModuleResults0, Results1)
+			), R)
+		    ), Result, !IO),
+		(
+			Result = succeeded(ModuleResults)
+		;
+			Result = failed,
+			ModuleResults = ModuleResults0
+		;
+			Result = exception(_),
+			% XXX Report error.
+			ModuleResults = ModuleResults0
+		),
+		io__set_input_stream(OldStream, _, !IO),
+		io__close_input(Stream, !IO)
+	;
+		OpenResult = error(_),
+		ModuleResults = ModuleResults0
+	).
+
+:- pred read_analysis_file_2(parse_entry(T)::in(parse_entry),
+	T::in, T::out, io__state::di, io__state::uo) is det.
+
+read_analysis_file_2(ParseEntry, Results0, Results, !IO) :-
+	parser__read_term(TermResult `with_type` read_term, !IO),
+	(
+		TermResult = term(_, term__functor(
+				term__integer(version_number), [], _))
+	->
+		true
+	;
+		throw(invalid_analysis_file)
+	),
+	read_analysis_file_3(ParseEntry, Results0, Results, !IO).
+
+:- pred read_analysis_file_3(parse_entry(T)::in(parse_entry),
+	T::in, T::out, io__state::di, io__state::uo) is det.
+
+read_analysis_file_3(ParseEntry, Results0, Results, !IO) :-
+	parser__read_term(TermResult, !IO),
+	(
+		TermResult = term(_, Term) `with_type` read_term,
+		ParseEntry(Term, Results0, Results)
+	;
+		TermResult = eof,
+		Results = Results0
+	;
+		TermResult = error(_, _),
+		throw(invalid_analysis_file)
+	).
+
+write_module_analysis_results(Info, ModuleId, ModuleResults, !IO) :-
+	write_analysis_file(Info ^ compiler, ModuleId, ".analysis",
+		write_result_entry, ModuleResults, !IO).
+
+write_module_analysis_requests(Info, ModuleId, ModuleRequests, !IO) :-
+	module_id_to_file_name(Info ^ compiler, ModuleId, ".request",
+		AnalysisFileName, !IO),
+	io__open_input(AnalysisFileName, InputResult, !IO),
+	( InputResult = ok(InputStream) ->
+		io__set_input_stream(InputStream, OldInputStream, !IO),
+		parser__read_term(VersionResult `with_type` read_term, !IO),
+		io__set_input_stream(OldInputStream, _, !IO),
+		io__close_input(InputStream, !IO),
+		(
+			VersionResult = term(_, term__functor(
+				term__integer(version_number), [], _))
+		->
+			io__open_append(AnalysisFileName, AppendResult, !IO),
+			( AppendResult = ok(AppendStream) ->
+				io__set_output_stream(AppendStream,
+					OldOutputStream, !IO),
+				write_analysis_entries(
+					write_request_entry(Info ^ compiler),
+					ModuleRequests, !IO),
+				io__set_output_stream(OldOutputStream, _, !IO),
+				io__close_output(AppendStream, !IO),
+				Appended = yes
+			;
+				Appended = no
+			)
+		;
+			Appended = no
+		)
+	;
+		Appended = no
+	),
+	( Appended = no ->
+		write_analysis_file(Info ^ compiler, ModuleId, ".request",
+			write_request_entry(Info ^ compiler),
+			ModuleRequests, !IO)
+	;
+		true
+	).
+
+:- pred write_result_entry `with_type` write_entry(analysis_result)
+		`with_inst` write_entry.
+
+write_result_entry(AnalysisName, FuncId,
+		analysis_result(_ `with_type` unit(FuncInfo), Call, Answer),
+		!IO) :-
+	VersionNumber = analysis_version_number(_ `with_type` FuncInfo,
+				Call, Answer), 
+	term_io__write_term_nl(varset__init `with_type` varset,
+		functor(atom(AnalysisName), [
+			functor(integer(VersionNumber), [], context_init),
+		    	functor(string(FuncId), [], context_init),
+			functor(string(to_string(Call)), [], context_init),
+			functor(string(to_string(Answer)), [], context_init)
+		], context_init), !IO).
+
+:- pred write_request_entry(Compiler::in)
+		`with_type` write_entry(analysis_request)
+		`with_inst` write_entry <= compiler(Compiler).
+
+write_request_entry(Compiler, AnalysisName, FuncId,
+		analysis_request(_, Call), !IO) :-
+	(
+		analysis_type(_ `with_type` unit(FuncInfo),
+			_ `with_type` unit(Call),
+			_ `with_type` unit(Answer)) =
+			analyses(Compiler, AnalysisName)
+	->
+		VersionNumber = analysis_version_number(
+			_ `with_type` FuncInfo, _ `with_type` Call,
+			_ `with_type`  Answer)
+	;
+		error("write_request_entry: unknown analysis type")
+
+	),
+	term_io__write_term_nl(varset__init `with_type` varset,
+		functor(atom(AnalysisName), [
+			functor(integer(VersionNumber), [], context_init),
+		    	functor(string(FuncId), [], context_init),
+			functor(string(to_string(Call)), [], context_init)
+		], context_init), !IO).
+
+:- type write_entry(T) == pred(analysis_name, func_id, T, io__state, io__state).
+:- inst write_entry == (pred(in, in, in, di, uo) is det).
+
+:- pred write_analysis_file(Compiler::in, module_id::in, string::in,
+	write_entry(T)::in(write_entry), module_analysis_map(T)::in,
+	io__state::di, io__state::uo) is det <= compiler(Compiler).
+
+write_analysis_file(Compiler, ModuleId, Suffix,
+		WriteEntry, ModuleResults, !IO) :-
+	module_id_to_file_name(Compiler, ModuleId,
+		Suffix, AnalysisFileName, !IO),
+	io__open_output(AnalysisFileName, OpenResult, !IO),
+	(
+		OpenResult = ok(Stream),
+		io__set_output_stream(Stream, OldOutput, !IO),
+		io__write_int(version_number, !IO),
+		io__write_string(".\n", !IO),
+		write_analysis_entries(WriteEntry, ModuleResults, !IO),
+		io__set_output_stream(OldOutput, _, !IO),
+		io__close_output(Stream, !IO)
+	;
+		OpenResult = error(Msg),
+		io__write_string("Error opening ", !IO),
+		io__write_string(AnalysisFileName, !IO),
+		io__write_string(" for output: ", !IO),
+		io__write_string(io__error_message(Msg), !IO),
+		io__nl(!IO)
+	).
+
+:- pred write_analysis_entries(write_entry(T)::in(write_entry),
+	module_analysis_map(T)::in, io__state::di, io__state::uo) is det.
+
+write_analysis_entries(WriteEntry, ModuleResults, !IO) :-
+	map__foldl(
+	    (pred(AnalysisName::in, FuncResults::in, di, uo) is det -->
+		map__foldl(
+		    (pred(FuncId::in, FuncResultList::in, di, uo) is det -->
+			list__foldl(
+			    (pred(FuncResult::in, di, uo) is det -->
+				WriteEntry(AnalysisName, FuncId, FuncResult)
+		    	    ), FuncResultList)
+		    ), FuncResults)
+	    ), ModuleResults, !IO).
+
+empty_request_file(Info, ModuleId, !IO) :-
+	module_id_to_file_name(Info ^ compiler, ModuleId, ".request", 
+		RequestFileName, !IO),
+	io__remove_file(RequestFileName, _, !IO).
+
Index: analysis/analysis.m
===================================================================
RCS file: analysis/analysis.m
diff -N analysis/analysis.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ analysis/analysis.m	27 Aug 2002 05:45:35 -0000
@@ -0,0 +1,334 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2002 University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+% File: analysis.m
+% Main author: stayl
+%
+% An inter-module analysis framework, as described in
+%
+%	Nicholas Nethercote. The Analysis Framework of HAL,
+%	Chapter 7: Inter-module Analysis, Master's Thesis,
+%	University of Melbourne, September 2001, revised April 2002.
+%	<http://www.cl.cam.ac.uk/~njn25/pubs/masters2001.ps.gz>.
+%
+%-----------------------------------------------------------------------------%
+:- module analysis.
+
+:- interface.
+
+:- import_module assoc_list, io, list, std_util.
+
+	% The intention is that eventually any compiler can
+	% use this library via .NET by defining an instance
+	% of this class.
+:- typeclass compiler(Compiler) where [
+	func compiler_name(Compiler) = string,
+
+	% Describe the analyses which can be performed by a compiler.
+	func analyses(Compiler, analysis_name) = analysis_type is semidet,
+
+	% module_id_to_file_name(Compiler, ModuleId, Ext, FileName)
+	pred module_id_to_file_name(Compiler::in, module_id::in,
+		string::in, string::out, io__state::di, io__state::uo) is det
+].
+
+:- type module_id == string.
+
+:- type analysis_name == string.
+
+:- type analysis_type
+	---> some [FuncInfo, Call, Answer]
+		analysis_type(unit(FuncInfo), unit(Call), unit(Answer))
+		=> analysis(FuncInfo, Call, Answer).
+
+	% An analysis is defined by a type describing call patterns,
+	% a type defining answer patterns and a type giving information
+	% about the function being analysed (e.g. arity) which should
+	% be provided by the caller.
+:- typeclass analysis(FuncInfo, Call, Answer) <=
+		(call_pattern(FuncInfo, Call),
+		answer_pattern(FuncInfo, Answer))
+	where
+[
+	func analysis_name(FuncInfo::unused, Call::unused, Answer::unused) =
+			(analysis_name::out) is det,
+
+	% The version number should be changed when the Call or Answer
+	% types are changed so that results which use the old types
+	% can be discarded.
+	func analysis_version_number(FuncInfo::unused, Call::unused,
+			Answer::unused) = (int::out) is det,
+
+	func preferred_fixpoint_type(FuncInfo::in,
+			Call::unused, Answer::unused) =
+			(fixpoint_type::out) is det
+].
+
+:- type fixpoint_type
+	--->
+			% Start at `bottom'.
+			% Must run to completion.
+		least_fixpoint
+	;
+			% Start at `top'.
+			% Can stop at any time.
+		greatest_fixpoint
+	.
+
+:- typeclass call_pattern(FuncInfo, Call)
+		<= (partial_order(FuncInfo, Call), to_string(Call)) where [].
+
+:- typeclass answer_pattern(FuncInfo, Answer)
+		<= (partial_order(FuncInfo, Answer), to_string(Answer)) where [
+	func bottom(FuncInfo) = Answer,
+	func top(FuncInfo) = Answer
+].
+
+:- typeclass partial_order(FuncInfo, Call) where [
+	pred more_precise_than(FuncInfo::in,
+			Call::in, Call::in) is semidet,
+	pred equivalent(FuncInfo::in,
+			Call::in, Call::in) is semidet
+].
+
+:- typeclass to_string(S) where [
+	func to_string(S) = string,
+	func from_string(string) = S is semidet
+].
+
+:- type any_call ---> any_call.
+:- instance call_pattern(unit, any_call).
+:- instance partial_order(unit, any_call).
+:- instance to_string(any_call).
+
+	% This will need to encode language specific details like
+	% whether it is a predicate or a function, and the arity
+	% and mode number.
+:- type func_id == string.
+
+	% Holds information used while analysing a module.
+:- type analysis_info.
+
+:- func init_analysis_info(Compiler) = analysis_info <= compiler(Compiler).
+
+	% Look up all results for a given function.
+:- pred lookup_results(module_id::in, func_id::in, FuncInfo::in,
+	assoc_list(Call, Answer)::out, analysis_info::in, analysis_info::out,
+	io__state::di, io__state::uo) is det
+	<= analysis(FuncInfo, Call, Answer).
+
+	% Look up the best result matching a given call.
+:- pred lookup_best_result(module_id::in, func_id::in, FuncInfo::in,
+	Call::in, maybe(pair(Call, Answer))::out, analysis_info::in,
+	analysis_info::out, io__state::di, io__state::uo) is det
+	<= analysis(FuncInfo, Call, Answer).
+
+	% Record an analysis result for a (usually local) function.
+:- pred record_result(module_id::in, func_id::in, FuncInfo::in, Call::in,
+	Answer::in, analysis_info::in, analysis_info::out) is det
+	<= analysis(FuncInfo, Call, Answer).
+
+	% Lookup all the requests for a given (usually local) function.
+:- pred lookup_requests(analysis_name::in, module_id::in, func_id::in,
+	FuncInfo::in, list(Call)::out, analysis_info::in, analysis_info::out,
+	io__state::di, io__state::uo) is det <= call_pattern(FuncInfo, Call).
+
+	% Record a request for a local function.
+:- pred record_request(analysis_name::in, module_id::in, func_id::in,
+	FuncInfo::in, Call::in, analysis_info::in, analysis_info::out) is det
+	<= call_pattern(FuncInfo, Call).
+
+	% Should be called after all analysis is completed to write the 
+	% requests and results for the current compilation to the
+	% analysis files.
+:- pred write_analysis_files(module_id::in, analysis_info::in,
+	io__state::di, io__state::uo) is det.
+
+:- implementation.
+
+:- include_module analysis__file.
+:- import_module analysis__file.
+
+:- import_module map, require, set.
+
+:- type analysis_info
+	---> some [Compiler] analysis_info(
+		compiler :: Compiler,
+		analysis_requests :: analysis_map(analysis_request),
+		analysis_results :: analysis_map(analysis_result)
+	) => compiler(Compiler).
+
+:- type analysis_result
+	--->	some [FuncInfo, Call, Answer] analysis_result(unit(FuncInfo),
+			Call, Answer) => analysis(FuncInfo, Call, Answer).
+
+:- type analysis_request
+	---> some [FuncInfo, Call] analysis_request(unit(FuncInfo), Call)
+			=> call_pattern(FuncInfo, Call).
+
+:- type analysis_hash == int.
+
+:- type analysis_map(T) == map(module_id, module_analysis_map(T)).
+:- type module_analysis_map(T) == map(analysis_name, func_analysis_map(T)).
+:- type func_analysis_map(T) == map(func_id, list(T)).
+
+:- instance call_pattern(unit, any_call) where [].
+:- instance partial_order(unit, any_call) where [
+	more_precise_than(_, _, _) :- semidet_fail,
+	equivalent(_, _, _) :- semidet_succeed
+].
+:- instance to_string(any_call) where [
+	to_string(any_call) = "",
+	from_string("") = any_call
+].
+
+init_analysis_info(Compiler) =
+	'new analysis_info'(Compiler, map__init, map__init).
+
+lookup_results(ModuleId, FuncId, _FuncInfo, ResultList, !Info, !IO) :-
+	%io__write_string("looking up results for ", !IO),
+	%io__write_string(FuncId, !IO),
+	%io__nl(!IO),
+	( map__search(!.Info ^ analysis_results, ModuleId, ModuleResults0) ->
+		ModuleResults = ModuleResults0
+	;
+		read_module_analysis_results(!.Info, ModuleId,
+			ModuleResults, !IO),
+		!:Info = !.Info ^ analysis_results
+				^ elem(ModuleId) := ModuleResults
+	),
+	AnalysisName = analysis_name(_ `with_type` FuncInfo,
+				_ `with_type` Call, _ `with_type` Answer),
+	(
+		Results = ModuleResults ^ elem(AnalysisName) ^ elem(FuncId)
+	->
+		ResultList = list__map(
+		    (func(Result) = ResultCall - ResultAnswer :-
+			Result = analysis_result(_,
+					ResultCall0, ResultAnswer0),
+			det_univ_to_type(univ(ResultCall0), ResultCall),
+		    	det_univ_to_type(univ(ResultAnswer0), ResultAnswer)
+		    ), Results)
+	;
+		ResultList = []
+	).
+
+lookup_best_result(ModuleId, FuncId, FuncInfo, Call,
+		MaybeBestResult, !Info, !IO) :-
+	%io__write_string("looking up best result for ", !IO),
+	%io__write_string(FuncId, !IO),
+	%io__nl(!IO),
+	lookup_results(ModuleId, FuncId, FuncInfo, ResultList, !Info, !IO),
+	MatchingResults = list__filter(
+		(pred((ResultCall - _)::in) is semidet :-
+			( more_precise_than(FuncInfo, Call, ResultCall)
+			; equivalent(FuncInfo, Call, ResultCall)
+			)
+		), ResultList),
+	(
+		MatchingResults = [],
+		MaybeBestResult = no
+	;
+		MatchingResults = [FirstResult | MatchingResults1],
+		MaybeBestResult = yes(list__foldl(
+		    (func(ThisResult, BestResult) =
+			(
+				more_precise_than(FuncInfo,
+					snd(ThisResult), snd(BestResult))
+			->
+				ThisResult
+			;
+				BestResult
+			)
+		    ), MatchingResults1, FirstResult))
+	).
+
+record_result(ModuleId, FuncId, FuncInfo, CallPattern, AnswerPattern, !Info) :-
+	( ModuleResults0 = map__search(!.Info ^ analysis_results, ModuleId) ->
+		ModuleResults1 = ModuleResults0
+	;
+		ModuleResults1 = map__init
+	),
+	AnalysisName = analysis_name(FuncInfo, CallPattern, AnswerPattern),
+	( AnalysisResults0 = map__search(ModuleResults1, AnalysisName) ->
+		AnalysisResults1 = AnalysisResults0
+	;
+		AnalysisResults1 = map__init
+	),
+	( FuncResults0 = map__search(AnalysisResults1, FuncId) ->
+		FuncResults1 = FuncResults0
+	;
+		FuncResults1 = []
+	),
+	!:Info = !.Info ^ analysis_results :=
+		map__set(!.Info ^ analysis_results, ModuleId,
+		map__set(ModuleResults1, AnalysisName,
+		map__set(AnalysisResults1, FuncId,
+		['new analysis_result'(unit1 `with_type` unit(FuncInfo),
+			CallPattern, AnswerPattern) | FuncResults1]))).
+
+lookup_requests(AnalysisName, ModuleId, FuncId, _FuncInfo,
+		CallPatterns, !Info, !IO) :-
+	( map__search(!.Info ^ analysis_requests, ModuleId, ModuleRequests0) ->
+		ModuleRequests = ModuleRequests0
+	;
+		read_module_analysis_requests(!.Info,
+			ModuleId, ModuleRequests, !IO),
+		!:Info = !.Info ^ analysis_requests
+				^ elem(ModuleId) := ModuleRequests
+	),
+	( CallPatterns0 = ModuleRequests ^ elem(AnalysisName) ^ elem(FuncId) ->
+		CallPatterns = list__filter_map(
+		    (func(Call0) = Call is semidet :-
+			univ(Call) = univ(Call0)
+		    ), CallPatterns0)
+	;
+		CallPatterns = []
+	).
+
+record_request(AnalysisName, ModuleId, FuncId, _FuncInfo,
+		CallPattern, !Info) :-
+	( ModuleResults0 = map__search(!.Info ^ analysis_requests, ModuleId) ->
+		ModuleResults1 = ModuleResults0
+	;
+		ModuleResults1 = map__init
+	),
+	( AnalysisResults0 = map__search(ModuleResults1, AnalysisName) ->
+		AnalysisResults1 = AnalysisResults0
+	;
+		AnalysisResults1 = map__init
+	),
+	( FuncResults0 = map__search(AnalysisResults1, FuncId) ->
+		FuncResults1 = FuncResults0
+	;
+		FuncResults1 = []
+	),
+	!:Info = !.Info ^ analysis_requests :=
+		map__set(!.Info ^ analysis_requests, ModuleId,
+		map__set(ModuleResults1, AnalysisName,
+		map__set(AnalysisResults1, FuncId,
+		['new analysis_request'(unit1 `with_type` unit(FuncInfo),
+			CallPattern) | FuncResults1]))).
+
+write_analysis_files(ModuleId, Info, !IO) :-
+	
+	%
+	% Write the results for the current module.
+	%
+	( ModuleResults0 = map__search(Info ^ analysis_results, ModuleId) ->
+		ModuleResults = ModuleResults0
+	;
+		ModuleResults = map__init
+	),
+	write_module_analysis_results(Info, ModuleId, ModuleResults, !IO),
+
+	%
+	% Write the requests for the imported modules.
+	%
+	map__foldl(write_module_analysis_requests(Info),
+		Info ^ analysis_requests, !IO),
+		
+	empty_request_file(Info, ModuleId, !IO).
+
Index: analysis/mer_analysis.m
===================================================================
RCS file: analysis/mer_analysis.m
diff -N analysis/mer_analysis.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ analysis/mer_analysis.m	27 Aug 2002 08:42:18 -0000
@@ -0,0 +1,14 @@
+%-----------------------------------------------------------------------------%
+% Copyright (C) 2002 University of Melbourne.
+% This file may only be copied under the terms of the GNU General
+% Public License - see the file COPYING in the Mercury distribution.
+%-----------------------------------------------------------------------------%
+% File: mer_analysis.m
+% Main author: stayl
+%
+% This module exists to make the name of the inter-module analysis
+% library matches the usual naming convention.
+%-----------------------------------------------------------------------------%
+:- module mer_analysis.
+
+:- import_module analysis.
Index: compiler/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/Mmakefile,v
retrieving revision 1.60
diff -u -u -r1.60 Mmakefile
--- compiler/Mmakefile	8 Aug 2002 07:22:36 -0000	1.60
+++ compiler/Mmakefile	8 Aug 2002 16:25:19 -0000
@@ -22,7 +22,7 @@
 
 MERCURY_MAIN_MODULES = top_level mlds_to_gcc
 
-VPATH=$(LIBRARY_DIR) $(BROWSER_DIR)
+VPATH=$(LIBRARY_DIR) $(BROWSER_DIR) $(ANALYSIS_DIR)
 
 #-----------------------------------------------------------------------------#
 
@@ -40,10 +40,13 @@
 GCC_BACKEND_LIBS =
 endif
 
-MCFLAGS +=	-I $(BROWSER_DIR)
-MLOBJS :=	../main.$O $(MLOBJS)
+MCFLAGS +=	-I $(BROWSER_DIR) -I $(ANALYSIS_DIR) \
+			--c-include-directory $(ANALYSIS_DIR)
+CFLAGS +=	-I $(ANALYSIS_DIR)
+MLOBJS :=	../main.$O ../analysis/lib$(ANALYSIS_LIB_NAME).$A $(MLOBJS)
 ALL_MLLIBS =	$(MLLIBS) $(EXTRA_MLLIBS) $(GCC_BACKEND_LIBS)
 MLFLAGS +=	--no-main --shared
+C2INITARGS +=	$(ANALYSIS_DIR)/$(ANALYSIS_LIB_NAME).init
 
 #
 # Work-around for a fixed limit: on alpha-dec-osf3.2, if we compile with
@@ -215,6 +218,7 @@
 $(MC_PROG): $(LIBRARY_DIR)/lib$(STD_LIB_NAME).$A
 $(MC_PROG): $(BROWSER_DIR)/lib$(BROWSER_LIB_NAME).$A
 $(MC_PROG): $(TRACE_DIR)/lib$(TRACE_LIB_NAME).$A
+$(MC_PROG): $(ANALYSIS_DIR)/lib$(ANALYSIS_LIB_NAME).$A
 # Should also depend on $(BOEHM_GC_DIR)/libgc(_prof).$A, but only
 # if in .gc(.prof) grade; GNU make does not support dynamic dependencies,
 # so just leave it out.
Index: compiler/hlds_module.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/hlds_module.m,v
retrieving revision 1.76
diff -u -u -r1.76 hlds_module.m
--- compiler/hlds_module.m	1 Aug 2002 11:52:17 -0000	1.76
+++ compiler/hlds_module.m	20 Aug 2002 11:01:03 -0000
@@ -26,12 +26,15 @@
 :- import_module hlds__hlds_pred, hlds__hlds_data, check_hlds__unify_proc.
 :- import_module hlds__special_pred.
 :- import_module libs__globals, backend_libs__foreign.
+:- import_module analysis.
 :- import_module relation, map, std_util, list, set, multi_map, counter.
 
 :- implementation.
 
 :- import_module hlds__hlds_out, parse_tree__prog_out, parse_tree__prog_util.
 :- import_module check_hlds__typecheck, parse_tree__modules.
+:- import_module transform_hlds__mmc_analysis.
+
 :- import_module bool, require, int, string.
 
 %-----------------------------------------------------------------------------%
@@ -373,6 +376,13 @@
 		no_tag_type_table, module_info).
 :- mode module_info_set_no_tag_types(in, in, out) is det.
 
+:- pred module_info_analysis_info(module_info, analysis_info).
+:- mode module_info_analysis_info(in, out) is det.
+
+:- pred module_info_set_analysis_info(module_info,
+		analysis_info, module_info).
+:- mode module_info_set_analysis_info(in, in, out) is det.
+
 %-----------------------------------------------------------------------------%
 
 :- pred module_info_preds(module_info, pred_table).
@@ -573,12 +583,16 @@
 		type_spec_info ::		type_spec_info,
 					% data used for user-guided type
 					% specialization.
-		no_tag_type_table ::		no_tag_type_table
+		no_tag_type_table ::		no_tag_type_table,
 					% Information about no tag
 					% types. This information is
 					% also in the type_table,
 					% but lookups in this table
 					% will be much faster.
+
+		analysis_info ::		analysis_info
+					% Information for the inter-module
+					% analysis framework.
 	).
 
 	% A predicate which creates an empty module
@@ -619,7 +633,7 @@
 	ModuleSubInfo = module_sub(Name, Globals, no, [], [], [], no, 0, 0, [], 
 		[], StratPreds, UnusedArgInfo, 0, ImportedModules,
 		IndirectlyImportedModules, no_aditi_compilation,
-		TypeSpecInfo, NoTagTypes),
+		TypeSpecInfo, NoTagTypes, init_analysis_info(mmc)),
 	ModuleInfo = module(ModuleSubInfo, PredicateTable, Requests,
 		UnifyPredMap, QualifierInfo, Types, Insts, Modes, Ctors,
 		ClassTable, SuperClassTable, InstanceTable, AssertionTable,
@@ -701,6 +715,7 @@
 	MI ^ sub_info ^ do_aditi_compilation).
 module_info_type_spec_info(MI, MI ^ sub_info ^ type_spec_info).
 module_info_no_tag_types(MI, MI ^ sub_info ^ no_tag_type_table).
+module_info_analysis_info(MI, MI ^ sub_info ^ analysis_info).
 
 %-----------------------------------------------------------------------------%
 
@@ -749,6 +764,8 @@
 	MI ^ sub_info ^ type_spec_info := NewVal).
 module_info_set_no_tag_types(MI, NewVal,
 	MI ^ sub_info ^ no_tag_type_table := NewVal).
+module_info_set_analysis_info(MI, NewVal,
+	MI ^ sub_info ^ analysis_info := NewVal).
 
 %-----------------------------------------------------------------------------%
 
Index: compiler/mercury_compile.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/mercury_compile.m,v
retrieving revision 1.266
diff -u -u -r1.266 mercury_compile.m
--- compiler/mercury_compile.m	21 Aug 2002 11:27:14 -0000	1.266
+++ compiler/mercury_compile.m	22 Aug 2002 07:57:41 -0000
@@ -108,6 +108,10 @@
 :- import_module libs__timestamp.
 :- import_module make, make__options_file, backend_libs__compile_target_code.
 
+	% inter-module analysis framework
+:- import_module analysis.
+:- import_module transform_hlds__mmc_analysis.
+
 	% library modules
 :- import_module int, list, map, set, std_util, require, string, bool, dir.
 :- import_module library, getopt, set_bbbtree, term, varset, assoc_list.
@@ -1124,6 +1128,18 @@
 		% magic sets can report errors.
 		{ module_info_num_errors(HLDS50, NumErrors) },
 		( { NumErrors = 0 } ->
+
+		    globals__io_lookup_bool_option(intermodule_analysis,
+				IntermodAnalysis),
+		    ( { IntermodAnalysis = yes } ->
+			{ module_info_analysis_info(HLDS50, AnalysisInfo) },
+			analysis__write_analysis_files(
+				module_name_to_module_id(ModuleName),
+				AnalysisInfo)
+		    ;
+			[]
+		    ),
+
 		    mercury_compile__maybe_generate_rl_bytecode(HLDS50,
 				Verbose, MaybeRLFile),
 		    ( { Target = c ; Target = asm } ->
@@ -1138,6 +1154,7 @@
 		    ;
 		    	[]
 		    ),
+
 		    ( { AditiOnly = yes } ->
 		    	{ HLDS = HLDS50 }
 		    ; { Target = il } ->
Index: compiler/modules.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/modules.m,v
retrieving revision 1.246
diff -u -u -r1.246 modules.m
--- compiler/modules.m	22 Aug 2002 02:34:13 -0000	1.246
+++ compiler/modules.m	22 Aug 2002 07:57:48 -0000
@@ -3996,6 +3996,18 @@
 	io__write_string(DepStream, "\n"),
 
 	io__write_string(DepStream, MakeVarName),
+	io__write_string(DepStream, ".analysiss = "),
+	write_compact_dependencies_list(Modules, "$(analysiss_subdir)",
+					".analysis", Basis, DepStream),
+	io__write_string(DepStream, "\n"),
+
+	io__write_string(DepStream, MakeVarName),
+	io__write_string(DepStream, ".requests = "),
+	write_compact_dependencies_list(Modules, "$(requests_subdir)",
+					".request", Basis, DepStream),
+	io__write_string(DepStream, "\n"),
+
+	io__write_string(DepStream, MakeVarName),
 	io__write_string(DepStream, ".schemas = "),
 	write_compact_dependencies_list(Modules, "", ".base_schema",
 					Basis, DepStream),
@@ -4524,6 +4536,8 @@
 		"\t-echo $(", MakeVarName, ".int3s) | xargs rm -f\n",
 		"\t-echo $(", MakeVarName, ".opts) | xargs rm -f\n",
 		"\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, ".ds) | xargs rm -f\n",
 		"\t-echo $(", MakeVarName, ".module_deps) | xargs rm -f\n",
 		"\t-echo $(", MakeVarName, ".all_mhs) | xargs rm -f\n",
Index: compiler/options.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/options.m,v
retrieving revision 1.384
diff -u -u -r1.384 options.m
--- compiler/options.m	22 Aug 2002 07:36:46 -0000	1.384
+++ compiler/options.m	22 Aug 2002 07:57:50 -0000
@@ -366,6 +366,7 @@
 		;	opt_level
 		;	opt_space	% default is to optimize time
 		;	intermodule_optimization
+		;	intermodule_analysis
 		;	read_opt_files_transitively
 		;	use_opt_files
 		;	use_trans_opt_files
@@ -879,6 +880,7 @@
 	opt_level		-	int_special,
 	opt_space		-	special,
 	intermodule_optimization -	bool(no),
+	intermodule_analysis -		bool(no),
 	read_opt_files_transitively -	bool(yes),
 	use_opt_files		-	bool(no),
 	use_trans_opt_files	-	bool(no),
@@ -1434,6 +1436,7 @@
 long_option("optimise-space",		opt_space).
 long_option("intermodule-optimization", intermodule_optimization).
 long_option("intermodule-optimisation", intermodule_optimization).
+long_option("intermodule-analysis",	intermodule_analysis).
 long_option("read-opt-files-transitively", read_opt_files_transitively).
 long_option("use-opt-files",		use_opt_files).
 long_option("use-trans-opt-files",	use_trans_opt_files).
@@ -3068,6 +3071,10 @@
 		"\t`.trans_opt' files which are already built,",
 		"\te.g. those for the standard library, but do",
 		"\tnot build any others.",
+		"--intermodule-analysis",
+		"\tPerform analyses such as termination analysis and",
+		"\tunused argument elimination across module boundaries.",
+		"\tThis option is not yet fully implemented.",
 		"--split-c-files",
 		"\tGenerate each C function in its own C file,",
 		"\tso that the linker will optimize away unused code.",
Index: compiler/prog_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/prog_util.m,v
retrieving revision 1.57
diff -u -u -r1.57 prog_util.m
--- compiler/prog_util.m	9 Jul 2002 01:29:42 -0000	1.57
+++ compiler/prog_util.m	20 Aug 2002 11:01:03 -0000
@@ -166,6 +166,7 @@
 :- type new_pred_id
 	--->	counter(int, int)		% Line number, Counter
 	;	type_subst(tvarset, type_subst)
+	;	unused_args(list(int))
 	.
 
 %-----------------------------------------------------------------------------%
@@ -472,6 +473,9 @@
 				SubstStr)
 		)),
 		list_to_string(SubstToString, TypeSubst, PredIdStr)
+	;
+		NewPredId = unused_args(Args),
+		list_to_string(int_to_string, Args, PredIdStr)
 	),
 
 	string__format("%s__%s__%s__%s",
Index: compiler/transform_hlds.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/transform_hlds.m,v
retrieving revision 1.2
diff -u -u -r1.2 transform_hlds.m
--- compiler/transform_hlds.m	8 May 2002 18:21:53 -0000	1.2
+++ compiler/transform_hlds.m	20 Aug 2002 11:01:03 -0000
@@ -33,7 +33,7 @@
 :- include_module higher_order.
 :- include_module inlining.
 :- include_module deforest.
-   :- include_module pd_cost, pd_debug, pd_info, pd_term.
+   :- include_module constraint, pd_cost, pd_debug, pd_info, pd_term.
    :- include_module pd_util.
 :- include_module delay_construct.
 :- include_module unused_args.
@@ -43,8 +43,10 @@
 :- include_module dead_proc_elim.
 :- include_module const_prop.
 
+:- include_module mmc_analysis.
+
 % XXX The following modules are all currently unused.
-:- include_module constraint, transform.
+:- include_module transform.
 :- include_module lco.
 
 %-----------------------------------------------------------------------------%
Index: compiler/unused_args.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/unused_args.m,v
retrieving revision 1.77
diff -u -u -r1.77 unused_args.m
--- compiler/unused_args.m	28 Mar 2002 03:43:47 -0000	1.77
+++ compiler/unused_args.m	26 Aug 2002 14:47:23 -0000
@@ -44,16 +44,28 @@
 :- interface.
 
 :- import_module hlds__hlds_module.
+:- import_module analysis.
 :- import_module io.
 
 :- pred unused_args__process_module(module_info::in, module_info::out,
 				io__state::di, io__state::uo) is det.
 
+	% Instances used by mmc_analysis.m.
+:- type unused_args_answer.
+:- type unused_args_func_info.
+:- instance analysis(unused_args_func_info, any_call, unused_args_answer).
+:- instance partial_order(unused_args_func_info, any_call).
+:- instance call_pattern(unused_args_func_info, any_call).
+:- instance partial_order(unused_args_func_info, unused_args_answer).
+:- instance answer_pattern(unused_args_func_info, unused_args_answer).
+:- instance to_string(unused_args_answer).
+
 %-------------------------------------------------------------------------------
 :- implementation.
 
 :- import_module parse_tree__mercury_to_mercury, parse_tree__modules. 
 :- import_module parse_tree__prog_data, parse_tree__prog_out.
+:- import_module parse_tree__prog_util.
 :- import_module hlds__hlds_pred, hlds__hlds_goal, hlds__hlds_data.
 :- import_module hlds__hlds_out, hlds__instmap, hlds__make_hlds.
 :- import_module hlds__quantification, hlds__special_pred.
@@ -61,6 +73,7 @@
 :- import_module hlds__goal_util.
 :- import_module check_hlds__type_util, check_hlds__mode_util.
 :- import_module check_hlds__inst_match, check_hlds__polymorphism.
+:- import_module transform_hlds__mmc_analysis.
 :- import_module ll_backend__code_util.
 :- import_module libs__options, libs__globals.
 
@@ -87,24 +100,76 @@
 		warning_info(prog_context, string, int, list(int)).
 			% context, pred name, arity, list of args to warn 
 
+%-----------------------------------------------------------------------------%
+	% Types and instances used by mmc_analysis.m.
+
+	% The list of unused arguments is in sorted order.
+:- type unused_args_answer ---> unused_args(list(int)).
+
+:- instance analysis(unused_args_func_info, any_call,
+		unused_args_answer) where [
+	analysis_name(_, _, _) = "unused_args",
+	analysis_version_number(_, _, _) = 1,
+	preferred_fixpoint_type(_, _, _) = least_fixpoint
+].
+
+:- type unused_args_func_info ---> unused_args_func_info(arity).
+
+:- instance call_pattern(unused_args_func_info, any_call) where [].
+:- instance partial_order(unused_args_func_info, any_call) where [
+	(more_precise_than(_, _, _) :- semidet_fail),
+	(equivalent(_, _, _) :- semidet_succeed)
+].
+
+:- instance answer_pattern(unused_args_func_info, unused_args_answer) where [
+	bottom(unused_args_func_info(Arity)) = unused_args(1 `..` Arity),
+	top(_) = unused_args([])
+].
+:- instance partial_order(unused_args_func_info, unused_args_answer) where [
+	(more_precise_than(_, unused_args(Args1), unused_args(Args2)) :-
+		set__subset(sorted_list_to_set(Args2),
+			sorted_list_to_set(Args1))
+	),
+	equivalent(_, Args, Args)
+].
+
+:- instance to_string(unused_args_answer) where [
+	to_string(unused_args(Args)) =
+		string__join_list(" ", list__map(int_to_string, Args)),
+	(from_string(String) = unused_args(Args) :-
+	    {Digits, Args0} =  string__foldl(
+		(func(Char, {Digits0, Ints0}) =
+		    ( char__is_digit(Char) ->
+			{[Char | Digits0], Ints0}
+		    ;
+			{[], [string__det_to_int(
+				string__from_rev_char_list(Digits0)) | Ints0]}
+		    )
+		), String, {[], []}),
+	    Args = list__reverse([string__det_to_int(
+	    		string__from_rev_char_list(Digits)) | Args0])
+	)
+].
 
-unused_args__process_module(ModuleInfo0, ModuleInfo) -->
+%-----------------------------------------------------------------------------%
+
+unused_args__process_module(!ModuleInfo) -->
 	globals__io_lookup_bool_option(very_verbose, VeryVerbose),
-	{ init_var_usage(ModuleInfo0, VarUsage0, PredProcs, OptProcs) },
+	init_var_usage(VarUsage0, PredProcs, ProcCallInfo0, !ModuleInfo),
 	%maybe_write_string(VeryVerbose, "% Finished initialisation.\n"),
 
 	{ unused_args_pass(PredProcs, VarUsage0, VarUsage) },
 	%maybe_write_string(VeryVerbose, "% Finished analysis.\n"),
 
 	{ map__init(UnusedArgInfo0) },
-	{ get_unused_arg_info(ModuleInfo0, PredProcs, VarUsage,
+	{ get_unused_arg_info(!.ModuleInfo, PredProcs, VarUsage,
 					UnusedArgInfo0, UnusedArgInfo) },
 
 	{ map__keys(UnusedArgInfo, PredProcsToFix) },
 
 	globals__io_lookup_bool_option(make_optimization_interface, MakeOpt),
 	( { MakeOpt = yes } ->
-		{ module_info_name(ModuleInfo0, ModuleName) },
+		{ module_info_name(!.ModuleInfo, ModuleName) },
 		module_name_to_file_name(ModuleName, ".opt.tmp", no,
 				OptFileName),
 		io__open_append(OptFileName, OptFileRes),
@@ -123,7 +188,7 @@
 	globals__io_lookup_bool_option(warn_unused_args, DoWarn),
 	( { DoWarn = yes ; MakeOpt = yes } ->
 		{ set__init(WarnedPredIds0) },
-		output_warnings_and_pragmas(ModuleInfo0, UnusedArgInfo,
+		output_warnings_and_pragmas(!.ModuleInfo, UnusedArgInfo,
 			MaybeOptFile, DoWarn, PredProcsToFix, WarnedPredIds0)
 	;
 		[]
@@ -137,29 +202,22 @@
 	(
 		{ DoFixup = yes }
 	->
-		{ map__init(ProcCallInfo0) },
-		{ create_new_preds(PredProcsToFix, UnusedArgInfo,
-				ProcCallInfo0, ProcCallInfo1,
-				ModuleInfo0, ModuleInfo1) }, 
-		{ make_imported_unused_args_pred_infos(OptProcs,
-				ProcCallInfo1, ProcCallInfo,
-				ModuleInfo1, ModuleInfo2) },
+		list__foldl3(create_new_pred(UnusedArgInfo), PredProcsToFix,
+				ProcCallInfo0, ProcCallInfo, !ModuleInfo), 
 		% maybe_write_string(VeryVerbose, "% Finished new preds.\n"),
-		fixup_unused_args(VarUsage, PredProcs, ProcCallInfo,
-				ModuleInfo2, ModuleInfo3, VeryVerbose),
+		fixup_unused_args(VarUsage, PredProcs,
+			ProcCallInfo, !ModuleInfo, VeryVerbose),
 		% maybe_write_string(VeryVerbose, "% Fixed up goals.\n"),
 		{ map__is_empty(ProcCallInfo) ->
-			ModuleInfo = ModuleInfo3
+			true
 		;
 			% The dependencies have changed, so the dependency
 			% graph needs rebuilding.
-			module_info_clobber_dependency_info(ModuleInfo3,
-				ModuleInfo)
+			module_info_clobber_dependency_info(!ModuleInfo)
 		}
 	;
-		{ ModuleInfo = ModuleInfo0 }
+		[]
 	).
-	  
 
 %-------------------------------------------------------------------------------
 	% Initialisation section
@@ -170,96 +228,110 @@
 	% iteration over.
 	% OptPredProcList is a list of procedures for which we got
 	% unused argument information from .opt files.
-:- pred init_var_usage(module_info::in, var_usage::out,
-		pred_proc_list::out, pred_proc_list::out) is det.
+:- pred init_var_usage(var_usage::out, pred_proc_list::out,
+		proc_call_info::out, module_info::in, module_info::out,
+		io__state::di, io__state::uo) is det.
 
-init_var_usage(ModuleInfo, VarUsage, PredProcList, OptPredProcList) :-
+init_var_usage(VarUsage, PredProcList, ProcCallInfo, !ModuleInfo, !IO) :-
+	map__init(ProcCallInfo0),
 	map__init(VarUsage0),
-	module_info_predids(ModuleInfo, PredIds),
-	module_info_preds(ModuleInfo, PredTable),
-	module_info_unused_arg_info(ModuleInfo, UnusedArgInfo),
-	setup_local_var_usage(ModuleInfo, PredTable, PredIds, UnusedArgInfo,
-		VarUsage0, VarUsage, [], PredProcList, [], OptPredProcList).
-
-
+	module_info_predids(!.ModuleInfo, PredIds),
+	module_info_unused_arg_info(!.ModuleInfo, UnusedArgInfo),
+	setup_local_var_usage(PredIds, UnusedArgInfo,
+		VarUsage0, VarUsage, [], PredProcList,
+		ProcCallInfo0, ProcCallInfo, !ModuleInfo, !IO).
 
 	% setup args for the whole module.	
-:- pred setup_local_var_usage(module_info::in, pred_table::in,
-	list(pred_id)::in, unused_arg_info::in, var_usage::in,
-	var_usage::out, pred_proc_list::in, pred_proc_list::out,
-	pred_proc_list::in,  pred_proc_list::out) is det.
-
-setup_local_var_usage(_, _, [], _, VarUsage, VarUsage,
-		PredProcs, PredProcs, OptProcs, OptProcs).
-setup_local_var_usage(ModuleInfo, PredTable, [PredId | PredIds], UnusedArgInfo,
-		VarUsage0, VarUsage, PredProcList0, PredProcList,
-		OptProcList0, OptProcList) :-
-	map__lookup(PredTable, PredId, PredInfo),
+:- pred setup_local_var_usage(list(pred_id)::in, unused_arg_info::in,
+	var_usage::in, var_usage::out, pred_proc_list::in, pred_proc_list::out,
+	proc_call_info::in, proc_call_info::out, module_info::in,
+	module_info::out, io__state::di, io__state::uo) is det.
+
+setup_local_var_usage([], _, !VarUsage, !PredProcs,
+		!OptProcs, !ModuleInfo, !IO).
+setup_local_var_usage([PredId | PredIds], UnusedArgInfo,
+		!VarUsage, !PredProcList, !OptProcs, !ModuleInfo, !IO) :-
+	module_info_pred_info(!.ModuleInfo, PredId, PredInfo),
 		% The builtins use all their arguments.
 	( code_util__predinfo_is_builtin(PredInfo) ->
-		VarUsage1 = VarUsage0,
-		setup_local_var_usage(ModuleInfo, PredTable, PredIds,
-			UnusedArgInfo, VarUsage1, VarUsage, PredProcList0,
-			PredProcList, OptProcList0, OptProcList)
+		setup_local_var_usage(PredIds, UnusedArgInfo, !VarUsage,
+			!PredProcList, !OptProcs, !ModuleInfo, !IO)
 	;
 		pred_info_procids(PredInfo, ProcIds),
-		setup_pred_args(ModuleInfo, PredId, ProcIds, UnusedArgInfo,
-			VarUsage0, VarUsage1, PredProcList0, PredProcList1,
-			OptProcList0, OptProcList1),
-		setup_local_var_usage(ModuleInfo, PredTable, PredIds,
-			UnusedArgInfo, VarUsage1, VarUsage, PredProcList1,
-			PredProcList, OptProcList1, OptProcList)
+		setup_pred_args(PredId, ProcIds, UnusedArgInfo, !VarUsage,
+			!PredProcList, !OptProcs, !ModuleInfo, !IO),
+		setup_local_var_usage(PredIds, UnusedArgInfo, !VarUsage,
+			!PredProcList, !OptProcs, !ModuleInfo, !IO)
 	).
 
-
 	% setup args for a predicate
-:- pred setup_pred_args(module_info::in, pred_id::in, list(proc_id)::in,
-			unused_arg_info::in, var_usage::in, var_usage::out,
-			pred_proc_list::in, pred_proc_list::out,
-			pred_proc_list::in, pred_proc_list::out) is det.
-
-setup_pred_args(_, _, [], _, VarUsage, VarUsage,
-		PredProcs, PredProcs, OptProcs, OptProcs).
-setup_pred_args(ModuleInfo, PredId, [ProcId | Rest], UnusedArgInfo, VarUsage0,
-		VarUsage, PredProcs0, PredProcs, OptProcs0, OptProcs) :-
-	module_info_pred_proc_info(ModuleInfo, PredId, ProcId,
+:- pred setup_pred_args(pred_id::in, list(proc_id)::in, unused_arg_info::in,
+	var_usage::in, var_usage::out, pred_proc_list::in, pred_proc_list::out,
+	proc_call_info::in, proc_call_info::out, module_info::in,
+	module_info::out, io__state::di, io__state::uo) is det.
+
+setup_pred_args(_, [], _, !VarUsage, !PredProcs, !OptProcs, !ModuleInfo, !IO).
+setup_pred_args(PredId, [ProcId | Rest], UnusedArgInfo, !VarUsage,
+		!PredProcs, !OptProcs, !ModuleInfo, !IO) :-
+	module_info_pred_proc_info(!.ModuleInfo, PredId, ProcId,
 		PredInfo, ProcInfo),
 	map__init(VarDep0),
-	Proc = proc(PredId, ProcId),
+	globals__io_lookup_bool_option(intermodule_analysis, Intermod, !IO),
 	(
-		% Get the unused argument info from the .opt files.
-		% Don't use the .opt file info when we have the clauses
-		% (opt_imported preds) since we may be able to do better with
-		% the information in this module.
+		% Don't use the intermodule analysis info when we have the
+		% clauses (opt_imported preds) since we may be able to do
+		% better with the information in this module.
+		Intermod = yes,
 		pred_info_is_imported(PredInfo)
 	->
-		( map__search(UnusedArgInfo, Proc, UnusedArgs) ->
+		pred_info_module(PredInfo, PredModule),
+		pred_info_get_is_pred_or_func(PredInfo, PredOrFunc),
+		pred_info_name(PredInfo, PredName),
+		pred_info_arity(PredInfo, PredArity),
+		FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
+			PredName, PredArity, ProcId),
+		module_info_analysis_info(!.ModuleInfo, AnalysisInfo0),
+		lookup_best_result(module_name_to_module_id(PredModule),
+			FuncId, unused_args_func_info(PredArity),
+			any_call, MaybeBestResult,
+			AnalysisInfo0, AnalysisInfo, !IO),
+		module_info_set_analysis_info(!.ModuleInfo,
+			AnalysisInfo, !:ModuleInfo),
+		( MaybeBestResult = yes(_ - unused_args(UnusedArgs)) ->
 			proc_info_headvars(ProcInfo, HeadVars),
 			list__map(list__index1_det(HeadVars),
-				UnusedArgs, UnusedVars),
+					UnusedArgs, UnusedVars),
 			initialise_vardep(VarDep0, UnusedVars, VarDep),
-			map__set(VarUsage0, proc(PredId, ProcId),
-				VarDep, VarUsage1),
-			OptProcs1 = [proc(PredId, ProcId) | OptProcs0]
+			!:VarUsage = map__set(!.VarUsage,
+					proc(PredId, ProcId), VarDep),
+			globals__io_lookup_bool_option(optimize_unused_args,
+				Optimize, !IO),
+			( Optimize = yes ->
+				make_imported_unused_args_pred_info(
+					proc(PredId, ProcId), UnusedArgs,
+					!OptProcs, !ModuleInfo)
+			;
+				true
+			)
 		;
-			VarUsage1 = VarUsage0,
-			OptProcs1 = OptProcs0
-		),
-		PredProcs1 = PredProcs0
+			true
+		)
 	;
-		pred_info_is_pseudo_imported(PredInfo),
-		hlds_pred__in_in_unification_proc_id(ProcId)
+		(
+			pred_info_is_imported(PredInfo)
+		;
+			pred_info_is_pseudo_imported(PredInfo),
+			hlds_pred__in_in_unification_proc_id(ProcId)
+		)
 	->
-		PredProcs1 = PredProcs0,
-		OptProcs1 = OptProcs0,
-		VarUsage1 = VarUsage0
+		true
 	;
 		proc_info_vartypes(ProcInfo, VarTypes),
 		map__keys(VarTypes, Vars),
 		initialise_vardep(VarDep0, Vars, VarDep1),
-		setup_output_args(ModuleInfo, ProcInfo, VarDep1, VarDep2),
+		setup_output_args(!.ModuleInfo, ProcInfo, VarDep1, VarDep2),
 		
-		module_info_globals(ModuleInfo, Globals),
+		module_info_globals(!.ModuleInfo, Globals),
 		proc_interface_should_use_typeinfo_liveness(PredInfo, ProcId,
 			Globals, TypeInfoLiveness),
 		( TypeInfoLiveness = yes ->
@@ -272,14 +344,15 @@
 		),
 
 		proc_info_goal(ProcInfo, Goal - _),
-		Info = traverse_info(ModuleInfo, VarTypes),
+		Info = traverse_info(!.ModuleInfo, VarTypes),
 		traverse_goal(Info, Goal, VarDep3, VarDep),
-		map__set(VarUsage0, proc(PredId, ProcId), VarDep, VarUsage1),
-		PredProcs1 = [proc(PredId, ProcId) | PredProcs0],
-		OptProcs1 = OptProcs0
+		!:VarUsage = map__set(!.VarUsage,
+				proc(PredId, ProcId), VarDep),
+
+		!:PredProcs = [proc(PredId, ProcId) | !.PredProcs]
 	),
-	setup_pred_args(ModuleInfo, PredId, Rest, UnusedArgInfo, VarUsage1,
-		VarUsage, PredProcs1, PredProcs, OptProcs1, OptProcs).
+	setup_pred_args(PredId, Rest, UnusedArgInfo,
+		!VarUsage, !PredProcs, !OptProcs, !ModuleInfo, !IO).
 
 
 :- pred initialise_vardep(var_dep::in, list(prog_var)::in, var_dep::out) is det.
@@ -732,11 +805,7 @@
 					UnusedArgInfo0, UnusedArgInfo) :-
 	PredProc = proc(PredId, ProcId), 
 	map__lookup(VarUsage, PredProc, LocalVarUsage),
-
-	module_info_preds(ModuleInfo, Preds),
-	map__lookup(Preds, PredId, PredInfo),
-	pred_info_procedures(PredInfo, Procs),
-	map__lookup(Procs, ProcId, ProcInfo),
+	module_info_pred_proc_info(ModuleInfo, PredId, ProcId, _, ProcInfo),
 	proc_info_headvars(ProcInfo, HeadVars),
 	get_unused_arg_nos(LocalVarUsage, HeadVars, 1, UnusedArgs),
 	map__det_insert(UnusedArgInfo0, PredProc, UnusedArgs, UnusedArgInfo1),
@@ -765,40 +834,74 @@
 		% interface. 
 		% The other is that the next proc_id for a predicate is
 		% chosen based on the length of the list of proc_ids.
-:- pred create_new_preds(pred_proc_list::in, unused_arg_info::in,
+:- pred create_new_pred(unused_arg_info::in, pred_proc_id::in,
 		proc_call_info::in, proc_call_info::out,
-		module_info::in, module_info::out) is det.
-
-create_new_preds([], _UnusedArgInfo, ProcCallInfo, ProcCallInfo, Mod, Mod).
-create_new_preds([PredProc | PredProcs], UnusedArgInfo,
-		ProcCallInfo0, ProcCallInfo, ModuleInfo0, ModuleInfo) :- 
-	create_new_pred(PredProc, UnusedArgInfo, ProcCallInfo0, ProcCallInfo1,
-			ModuleInfo0, ModuleInfo1),
-	create_new_preds(PredProcs, UnusedArgInfo, ProcCallInfo1, ProcCallInfo,
-			ModuleInfo1, ModuleInfo).
+		module_info::in, module_info::out,
+		io__state::di, io__state::uo) is det.
 
-:- pred create_new_pred(pred_proc_id::in, unused_arg_info::in,
-		proc_call_info::in, proc_call_info::out,
-		module_info::in, module_info::out) is det.
-
-create_new_pred(proc(PredId, ProcId), UnusedArgInfo,
-		ProcCallInfo0, ProcCallInfo, ModuleInfo0, ModuleInfo) :-
+create_new_pred(UnusedArgInfo, proc(PredId, ProcId),
+		!ProcCallInfo, !ModuleInfo, !IO) :-
 	map__lookup(UnusedArgInfo, proc(PredId, ProcId), UnusedArgs),
-	module_info_pred_proc_info(ModuleInfo0, PredId, ProcId, PredInfo0,
-								OldProc0), 
+	module_info_pred_proc_info(!.ModuleInfo,
+		PredId, ProcId, OrigPredInfo, OrigProcInfo), 
 	(
 		UnusedArgs = []
 	->
-		ModuleInfo = ModuleInfo0,
-		ProcCallInfo = ProcCallInfo0
+		true
 	;
-		need_extra_proc(ModuleInfo0, UnusedArgs, proc(PredId, ProcId), 
-			IntermodUnusedArgs, InMap),
-		pred_info_import_status(PredInfo0, Status0),
+		pred_info_module(OrigPredInfo, PredModule),
+		pred_info_name(OrigPredInfo, PredName),
+
+		globals__io_lookup_bool_option(intermodule_analysis,
+			Intermod, !IO),
+		( Intermod = yes ->
+			module_info_analysis_info(!.ModuleInfo, AnalysisInfo0),
+			pred_info_get_is_pred_or_func(OrigPredInfo,
+				PredOrFunc),
+			pred_info_arity(OrigPredInfo, PredArity),
+			ModuleId = module_name_to_module_id(PredModule),
+			FuncId = pred_or_func_name_arity_to_func_id(PredOrFunc,
+					PredName, PredArity, ProcId),
+			FuncInfo = unused_args_func_info(PredArity),
+
+			lookup_results(ModuleId, FuncId, FuncInfo,
+				IntermodResults0, AnalysisInfo0, AnalysisInfo1,
+				!IO),
+			IntermodResults1 = list__map(
+			    (func(any_call - unused_args(ResArgs)) = ResArgs),
+			    IntermodResults0),
+			( list__member(UnusedArgs, IntermodResults1) ->
+				AnalysisInfo = AnalysisInfo1
+			;
+				analysis__record_result(ModuleId,
+					FuncId, FuncInfo, any_call,
+					unused_args(UnusedArgs),
+					AnalysisInfo1, AnalysisInfo)
+			),
+			module_info_set_analysis_info(!.ModuleInfo,
+				AnalysisInfo, !:ModuleInfo),
+
+			%
+			% XXX Mark versions which have more unused arguments
+			% than what we have computed here as invalid
+			% in the AnalysisInfo, so that modules which use
+			% those versions can be recompiled.
+			%
+			IntermodResults = list__filter(
+			    (pred(VersionUnusedArgs::in) is semidet :-
+				set__subset(
+					sorted_list_to_set(VersionUnusedArgs),
+					sorted_list_to_set(UnusedArgs))
+				), IntermodResults1)
+		;
+			IntermodResults0 = [],
+			IntermodResults = []
+		),
+		pred_info_import_status(OrigPredInfo, Status0),
 		(
 			Status0 = opt_imported,
-			InMap = yes,
-			IntermodUnusedArgs = no
+			IntermodResults0 = [_|_],
+			IntermodResults = []
 		->
 			% If this predicate is from a .opt file, and
 			% no more arguments have been removed than in the
@@ -807,129 +910,91 @@
 			% it if no other optimization is performed on it.
 			Status = opt_imported
 		;
-			Status0 = exported,
-			InMap = yes
+			status_is_exported(Status0, yes)
 		->
-			% This specialized version of the predicate was
-			% declared in the .opt file for this module so
+			% This specialized version of the predicate will be
+			% declared in the analysis file for this module so
 			% it must be exported.
-			Status = exported
+			Status = Status0
 		;
 			Status = local
 		),
-		( IntermodUnusedArgs = no ->
-			NameSuffix = "__ua"
-		;
-			% This predicate was declared in a .opt file,
-			% but more arguments were removed than was declared
-			% in the .opt file, so a different name is required. 
-			NameSuffix = "__uab"
-		),
-		make_new_pred_info(ModuleInfo0, PredInfo0, UnusedArgs,
-		    NameSuffix, Status, proc(PredId, ProcId), NewPredInfo0),
+		make_new_pred_info(!.ModuleInfo, OrigPredInfo, UnusedArgs,
+		    Status, proc(PredId, ProcId), NewPredInfo0),
+		pred_info_name(NewPredInfo0, NewPredName),
 		pred_info_procedures(NewPredInfo0, NewProcs0),
-		next_mode_id(NewProcs0, no, NewProcId),
 
 			% Assign the old procedure to a new predicate, which
 			% will be fixed up in fixup_unused_args.
-		map__set(NewProcs0, NewProcId, OldProc0, NewProcs),
+		map__set(NewProcs0, ProcId, OrigProcInfo, NewProcs),
 		pred_info_set_procedures(NewPredInfo0, NewProcs, NewPredInfo),
 
 			% add the new proc to the pred table
-		module_info_get_predicate_table(ModuleInfo0, PredTable0),
-		predicate_table_insert(PredTable0, NewPredInfo, NewPredId,
-								PredTable1),
-		pred_info_module(NewPredInfo, PredModule),
-		pred_info_name(NewPredInfo, PredName),
-		PredSymName = qualified(PredModule, PredName),
-			% add the new proc to the proc_call_info map
-		map__det_insert(ProcCallInfo0, proc(PredId, ProcId),
-		    call_info(NewPredId, NewProcId, PredSymName, UnusedArgs),
-		    ProcCallInfo),
-		(
-			Status0 = exported,
-			IntermodUnusedArgs = yes(UnusedArgs2)
-		->
-			% Add an exported predicate with the number of removed
-			% arguments promised in the .opt file which just calls
-			% the new predicate.
-			make_new_pred_info(ModuleInfo0, PredInfo0,
-				UnusedArgs2, "__ua", exported,
-				proc(PredId, ProcId), ExtraPredInfo0),
-			create_call_goal(UnusedArgs, NewPredId, NewProcId,
-				PredModule, PredName, OldProc0, ExtraProc0),
-			proc_info_headvars(OldProc0, HeadVars0),
-			remove_listof_elements(HeadVars0, 1, UnusedArgs2,
-				IntermodHeadVars),
-			proc_info_set_headvars(ExtraProc0, IntermodHeadVars,
-				ExtraProc1),
-			proc_info_argmodes(OldProc0, ArgModes0),
-			remove_listof_elements(ArgModes0, 1, UnusedArgs2,
-				IntermodArgModes),
-			proc_info_set_argmodes(ExtraProc1, IntermodArgModes,
-					ExtraProc),
-			pred_info_procedures(ExtraPredInfo0, ExtraProcs0),
-			next_mode_id(ExtraProcs0, no, ExtraProcId),
-			map__set(ExtraProcs0, ExtraProcId,
-				ExtraProc, ExtraProcs),
-			pred_info_set_procedures(ExtraPredInfo0, ExtraProcs,
-				ExtraPredInfo),
-			predicate_table_insert(PredTable1, ExtraPredInfo,
-					_, PredTable2)
-		;
-			PredTable2 = PredTable1
-		),
+		module_info_get_predicate_table(!.ModuleInfo, PredTable0),
+		predicate_table_insert(PredTable0,
+			NewPredInfo, NewPredId, PredTable),
+		module_info_set_predicate_table(!.ModuleInfo,
+			PredTable, !:ModuleInfo),
 
-		predicate_table_get_preds(PredTable2, Preds0),
-		pred_info_procedures(PredInfo0, Procs0),
-		create_call_goal(UnusedArgs, NewPredId, NewProcId,
-			PredModule, PredName, OldProc0, OldProc),
-		map__set(Procs0, ProcId, OldProc, Procs),
-		pred_info_set_procedures(PredInfo0, Procs, PredInfo),
-		map__det_update(Preds0, PredId, PredInfo, Preds1),
-		predicate_table_set_preds(PredTable2, Preds1, PredTable),
-		module_info_set_predicate_table(ModuleInfo0, PredTable,
-							ModuleInfo)
-	).
-
-	% Check that if this procedure has a pragma unused_args declaration 
-	% in the .opt file, the number of removed arguments matches with what
-	% has just been computed. If not, it means that more arguments were
-	% found to be unused given the information from .opt files, so we need
-	% to create an interface predicate with the promised number of
-	% arguments removed which calls the fully optimized version.
-:- pred need_extra_proc(module_info::in, list(int)::in, pred_proc_id::in,
-		maybe(list(int))::out, bool::out) is det.
-
-need_extra_proc(ModuleInfo, UnusedArgs, PredProcId,
-		MaybeIntermodUnusedArgs, InMap) :-
-	module_info_unused_arg_info(ModuleInfo, IntermodUnusedArgInfo),
-	(
-		map__search(IntermodUnusedArgInfo,
-			PredProcId, IntermodUnusedArgs)
-	->
-		InMap = yes,
-		( IntermodUnusedArgs = UnusedArgs ->
-			MaybeIntermodUnusedArgs = no
-		;
-			MaybeIntermodUnusedArgs = yes(IntermodUnusedArgs)
-		)
-	;
-		InMap = no,
-		MaybeIntermodUnusedArgs = no
+			% add the new proc to the proc_call_info map
+		PredSymName = qualified(PredModule, NewPredName),
+		map__det_insert(!.ProcCallInfo, proc(PredId, ProcId),
+			call_info(NewPredId, ProcId, PredSymName, UnusedArgs),
+			!:ProcCallInfo),
+
+			% add a forwarding predicate with the
+			% original interface.
+		create_call_goal(UnusedArgs, NewPredId, ProcId, PredModule,
+			NewPredName, OrigProcInfo, ForwardingProcInfo),
+		module_info_set_pred_proc_info(!.ModuleInfo,
+			PredId, ProcId, OrigPredInfo,
+			ForwardingProcInfo, !:ModuleInfo),
+
+			% add forwarding predicates for results
+			% produced in previous compilations.
+		list__foldl(
+			make_intermod_proc(PredId, NewPredId, ProcId,
+				NewPredName, OrigPredInfo, OrigProcInfo,
+				UnusedArgs),
+			IntermodResults, !ModuleInfo)
 	).
 
+:- pred make_intermod_proc(pred_id::in, pred_id::in, proc_id::in, string::in,
+	pred_info::in, proc_info::in, list(int)::in, list(int)::in,
+	module_info::in, module_info::out) is det.
+
+make_intermod_proc(PredId, NewPredId, ProcId, NewPredName,
+		OrigPredInfo, OrigProcInfo, UnusedArgs,
+		UnusedArgs2, !ModuleInfo) :-
+	% Add an exported predicate with the number of removed
+	% arguments promised in the analysis file which just calls
+	% the new predicate.
+	make_new_pred_info(!.ModuleInfo, OrigPredInfo, UnusedArgs2,
+		exported, proc(PredId, ProcId), ExtraPredInfo0),
+	pred_info_module(OrigPredInfo, PredModule),
+	create_call_goal(UnusedArgs, NewPredId, ProcId,
+		PredModule, NewPredName, OrigProcInfo, ExtraProc0),
+	proc_info_headvars(OrigProcInfo, HeadVars0),
+	remove_listof_elements(HeadVars0, 1, UnusedArgs2, IntermodHeadVars),
+	proc_info_set_headvars(ExtraProc0, IntermodHeadVars, ExtraProc1),
+	proc_info_argmodes(OrigProcInfo, ArgModes0),
+	remove_listof_elements(ArgModes0, 1, UnusedArgs2, IntermodArgModes),
+	proc_info_set_argmodes(ExtraProc1, IntermodArgModes, ExtraProc),
+	pred_info_procedures(ExtraPredInfo0, ExtraProcs0),
+	map__set(ExtraProcs0, ProcId, ExtraProc, ExtraProcs),
+	pred_info_set_procedures(ExtraPredInfo0, ExtraProcs, ExtraPredInfo),
+	module_info_get_predicate_table(!.ModuleInfo, PredTable0),
+	predicate_table_insert(PredTable0, ExtraPredInfo, _, PredTable),
+	module_info_set_predicate_table(!.ModuleInfo, PredTable, !:ModuleInfo).
+
 :- pred make_new_pred_info(module_info::in, pred_info::in, list(int)::in,
-	    string::in, import_status::in, pred_proc_id::in,
-	    pred_info::out) is det.
+	    import_status::in, pred_proc_id::in, pred_info::out) is det.
 
-make_new_pred_info(ModuleInfo, PredInfo0, UnusedArgs, NameSuffix, Status,
-		proc(_PredId, ProcId), PredInfo) :-
+make_new_pred_info(ModuleInfo, PredInfo0, UnusedArgs, Status,
+		proc(_PredId, _ProcId), PredInfo) :-
 	pred_info_module(PredInfo0, PredModule),
 	pred_info_name(PredInfo0, Name0),
 	pred_info_get_is_pred_or_func(PredInfo0, PredOrFunc),
-	proc_id_to_int(ProcId, ProcInt),
-	string__int_to_string(ProcInt, Id),
 	pred_info_arg_types(PredInfo0, Tvars, ExistQVars, ArgTypes0),
 		% create a unique new pred name using the old proc_id
 	(
@@ -961,7 +1026,8 @@
 	;
 		Name1 = Name0
 	),
-	string__append_list([Name1, NameSuffix, Id], Name),
+	make_pred_name(PredModule, "UnusedArgs", yes(PredOrFunc),
+		Name1, unused_args(UnusedArgs), Name),
 	pred_info_arity(PredInfo0, Arity),
 	pred_info_typevarset(PredInfo0, TypeVars),
 	remove_listof_elements(ArgTypes0, 1, UnusedArgs, ArgTypes),
@@ -972,9 +1038,7 @@
 	pred_info_get_class_context(PredInfo0, ClassContext),
 	pred_info_get_aditi_owner(PredInfo0, Owner),
 	map__init(EmptyProofs),
-		% *** This will need to be fixed when the condition
-		%	field of the pred_info becomes used.
-	pred_info_init(PredModule, qualified(PredModule, Name), Arity, Tvars,
+	pred_info_init(PredModule, Name, Arity, Tvars,
 		ExistQVars, ArgTypes, true, Context, ClausesInfo, Status,
 		Markers, GoalType, PredOrFunc, ClassContext, EmptyProofs,
 		Owner, PredInfo1),
@@ -1018,23 +1082,18 @@
 
 	% Create a pred_info for an imported pred with a pragma unused_args
 	% in the .opt file.
-:- pred make_imported_unused_args_pred_infos(pred_proc_list::in,
+:- pred make_imported_unused_args_pred_info(pred_proc_id::in, list(int)::in,
 		proc_call_info::in, proc_call_info::out,
 		module_info::in, module_info::out) is det.
 
-make_imported_unused_args_pred_infos([], ProcCallInfo, ProcCallInfo,
-				ModuleInfo, ModuleInfo).
-make_imported_unused_args_pred_infos([OptProc | OptProcs],
+make_imported_unused_args_pred_info(OptProc, UnusedArgs,
  		ProcCallInfo0, ProcCallInfo, ModuleInfo0, ModuleInfo) :-
-	module_info_unused_arg_info(ModuleInfo0, UnusedArgInfo),
-	map__lookup(UnusedArgInfo, OptProc, UnusedArgs),
 	OptProc = proc(PredId, ProcId),
 	module_info_pred_proc_info(ModuleInfo0,
 		PredId, ProcId, PredInfo0, ProcInfo0),
 	make_new_pred_info(ModuleInfo0, PredInfo0, UnusedArgs,
-		"__ua", imported(interface), OptProc, NewPredInfo0),
+		imported(interface), OptProc, NewPredInfo0),
 	pred_info_procedures(NewPredInfo0, NewProcs0),
-	next_mode_id(NewProcs0, no, NewProcId),
 
 		% Assign the old procedure to a new predicate.
 	proc_info_headvars(ProcInfo0, HeadVars0),
@@ -1043,23 +1102,20 @@
 	proc_info_argmodes(ProcInfo1, ArgModes0),
 	remove_listof_elements(ArgModes0, 1, UnusedArgs, ArgModes),
 	proc_info_set_argmodes(ProcInfo0, ArgModes, ProcInfo),
-	map__set(NewProcs0, NewProcId, ProcInfo, NewProcs),
+	map__set(NewProcs0, ProcId, ProcInfo, NewProcs),
 	pred_info_set_procedures(NewPredInfo0, NewProcs, NewPredInfo),
 
 		% Add the new proc to the pred table.
 	module_info_get_predicate_table(ModuleInfo0, PredTable0),
 	predicate_table_insert(PredTable0, NewPredInfo, NewPredId, PredTable1),
-	module_info_set_predicate_table(ModuleInfo0, PredTable1, ModuleInfo1),
+	module_info_set_predicate_table(ModuleInfo0, PredTable1, ModuleInfo),
 	pred_info_module(NewPredInfo, PredModule),
 	pred_info_name(NewPredInfo, PredName),
 	PredSymName = qualified(PredModule, PredName),
 		% Add the new proc to the proc_call_info map.
 	map__det_insert(ProcCallInfo0, proc(PredId, ProcId),
-		call_info(NewPredId, NewProcId, PredSymName, UnusedArgs),
-		ProcCallInfo1),
-	make_imported_unused_args_pred_infos(OptProcs, ProcCallInfo1,
-		ProcCallInfo, ModuleInfo1, ModuleInfo).
-
+		call_info(NewPredId, ProcId, PredSymName, UnusedArgs),
+		ProcCallInfo).
 
 :- pred remove_listof_elements(list(T)::in, int::in, list(int)::in,
 							 list(T)::out) is det.
Index: doc/user_guide.texi
===================================================================
RCS file: /home/mercury1/repository/mercury/doc/user_guide.texi,v
retrieving revision 1.322
diff -u -u -r1.322 user_guide.texi
--- doc/user_guide.texi	22 Aug 2002 07:36:51 -0000	1.322
+++ doc/user_guide.texi	22 Aug 2002 07:58:07 -0000
@@ -5076,6 +5076,12 @@
 already built, e.g.@: those for the standard library, but do not build any
 others.
 
+ at item --intermodule-analysis
+ at findex --intermodule-analysis
+Perform analyses such as termination analysis and
+unused argument elimination across module boundaries.
+This option is not yet fully implemented.
+
 @sp 1
 @item --split-c-files
 @findex --split-c-files
Index: library/std_util.m
===================================================================
RCS file: /home/mercury1/repository/mercury/library/std_util.m,v
retrieving revision 1.272
diff -u -u -r1.272 std_util.m
--- library/std_util.m	21 Jun 2002 13:26:50 -0000	1.272
+++ library/std_util.m	26 Aug 2002 15:30:24 -0000
@@ -135,6 +135,8 @@
 
 :- type unit		--->	unit.
 
+:- type unit(T)		--->	unit1.
+
 %-----------------------------------------------------------------------------%
 
 % The "pair" type.  Useful for many purposes.
Index: profiler/demangle.m
===================================================================
RCS file: /home/mercury1/repository/mercury/profiler/demangle.m,v
retrieving revision 1.15
diff -u -u -r1.15 demangle.m
--- profiler/demangle.m	25 Sep 2001 09:37:07 -0000	1.15
+++ profiler/demangle.m	8 Aug 2002 15:30:46 -0000
@@ -157,6 +157,8 @@
 	% Process the mangling introduced by unused_args.m.
 	% This involves stripping off the `__ua<m>' or `__uab<m>' added to 
 	% the end of the predicate/function name, where m is the mode number.
+	% XXX This is out-of-date. The compiler now generates names
+	% such as UnusedArgs__p__[1].
 	% 
 	(
 		remove_trailing_int(UA_ModeNum),
Index: scripts/Mmake.vars.in
===================================================================
RCS file: /home/mercury1/repository/mercury/scripts/Mmake.vars.in,v
retrieving revision 1.79
diff -u -u -r1.79 Mmake.vars.in
--- scripts/Mmake.vars.in	22 Aug 2002 02:34:21 -0000	1.79
+++ scripts/Mmake.vars.in	22 Aug 2002 07:59:11 -0000
@@ -556,6 +556,8 @@
 int3s_subdir=$(SUBDIR)int3s/
 opts_subdir=$(SUBDIR)opts/
 trans_opts_subdir=$(SUBDIR)trans_opts/
+analysiss_subdir=$(SUBDIR)analysiss/
+requests_subdir=$(SUBDIR)requests/
 date0s_subdir=$(SUBDIR)date0s/
 dates_subdir=$(SUBDIR)dates/
 date3s_subdir=$(SUBDIR)date3s/
@@ -596,6 +598,8 @@
 int3s_subdir=
 opts_subdir=
 trans_opts_subdir=
+analysiss_subdir=
+requests_subdir=
 date0s_subdir=
 dates_subdir=
 date3s_subdir=
Index: tests/warnings/Mercury.options
===================================================================
RCS file: /home/mercury1/repository/tests/warnings/Mercury.options,v
retrieving revision 1.1
diff -u -u -r1.1 Mercury.options
--- tests/warnings/Mercury.options	17 Aug 2002 13:52:33 -0000	1.1
+++ tests/warnings/Mercury.options	20 Aug 2002 09:58:27 -0000
@@ -12,6 +12,9 @@
 					--trace-optimized
 
 MCFLAGS-duplicate_call		= --warn-duplicate-calls
+MCFLAGS-unused_args_analysis    = --intermodule-analysis \
+				--optimize-unused-args --warn-unused-args
+MCFLAGS-unused_args_analysis2   = --intermodule-analysis --optimize-unused-args
 MCFLAGS-unused_args_test	= --warn-unused-args
 MCFLAGS-unused_import		= --warn-interface-imports
 MCFLAGS-inference_test		= --infer-all
Index: tests/warnings/Mmakefile
===================================================================
RCS file: /home/mercury1/repository/tests/warnings/Mmakefile,v
retrieving revision 1.22
diff -u -u -r1.22 Mmakefile
--- tests/warnings/Mmakefile	17 Aug 2002 13:52:33 -0000	1.22
+++ tests/warnings/Mmakefile	20 Aug 2002 09:57:54 -0000
@@ -20,6 +20,7 @@
 	simple_code \
 	singleton_test \
 	state_vars_test \
+	unused_args_analysis \
 	unused_args_test \
 	unused_import
 
@@ -41,6 +42,11 @@
 $(COMPILE_PROGS:%=%.runtest): %.runtest: %.res_compile ;
 
 $(ERRORCHECK_PROGS:%=%.runtest): %.runtest: %.res_error ;
+
+	# Build the `.analysis' file for unused_args_analysis2
+	# before building unused_args_analysis.c.
+$(cs_subdir)unused_args_analysis.c: $(cs_subdir)unused_args_analysis2.c
+unused_args_analysis.err: $(cs_subdir)unused_args_analysis2.c
 
 #-----------------------------------------------------------------------------#
 
Index: tests/warnings/unused_args_analysis.exp
===================================================================
RCS file: tests/warnings/unused_args_analysis.exp
diff -N tests/warnings/unused_args_analysis.exp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/warnings/unused_args_analysis.exp	8 Aug 2002 10:57:53 -0000
@@ -0,0 +1,2 @@
+unused_args_analysis.m:005: In predicate `unused_args_analysis:p/2':
+unused_args_analysis.m:005:   warning: argument 1 is unused.
Index: tests/warnings/unused_args_analysis.m
===================================================================
RCS file: tests/warnings/unused_args_analysis.m
diff -N tests/warnings/unused_args_analysis.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/warnings/unused_args_analysis.m	1 Aug 2002 15:53:47 -0000
@@ -0,0 +1,11 @@
+:- module unused_args_analysis.
+
+:- interface.
+
+:- pred p(int::in, int::out) is det.
+
+:- implementation.
+
+:- import_module unused_args_analysis2.
+
+p(X, Y) :- p2(X, Y).
Index: tests/warnings/unused_args_analysis2.m
===================================================================
RCS file: tests/warnings/unused_args_analysis2.m
diff -N tests/warnings/unused_args_analysis2.m
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ tests/warnings/unused_args_analysis2.m	1 Aug 2002 08:56:23 -0000
@@ -0,0 +1,9 @@
+:- module unused_args_analysis2.
+
+:- interface.
+
+:- pred p2(int::in, int::out) is det.
+
+:- implementation.
+
+p2(_, 1).
Index: tools/bootcheck
===================================================================
RCS file: /home/mercury1/repository/mercury/tools/bootcheck,v
retrieving revision 1.136
diff -u -u -r1.136 bootcheck
--- tools/bootcheck	21 Aug 2002 11:27:56 -0000	1.136
+++ tools/bootcheck	22 Aug 2002 07:59:40 -0000
@@ -484,6 +484,13 @@
 		cp $root/browser/Mmake* $root/browser/Mercury.options .
 		$LN_S $root/browser/$BROWSER_LIB_NAME.init .
 		cd $root/stage2
+		mkdir analysis
+		cd analysis
+		$LN_S $root/analysis/*.m .
+		cp $root/analysis/Mmake* $root/analysis/Mercury.options .
+		$LN_S $root/analysis/$ANALYSIS_LIB_NAME.init .
+
+		cd $root/stage2
 		if $copy_runtime
 		then
 			# Remove symbolic link to the stage 1 runtime
@@ -604,7 +611,8 @@
 		fi
 
 		if (cd stage2 && $MMAKE $mmake_opts dep_library dep_browser \
-			dep_compiler dep_profiler dep_deep_profiler)
+			dep_analysis dep_compiler dep_profiler \
+			dep_deep_profiler)
 		then
 			echo "building of stage 2 dependencies successful"
 		else
@@ -636,6 +644,14 @@
 			exit 1
 		fi
 
+		if (cd stage2 && $MMAKE $mmake_opts $jfactor analysis)
+		then
+			echo "building of stage 2 analysis successful"
+		else
+			echo "building of stage 2 analysis not successful"
+			exit 1
+		fi
+
 		if (cd stage2/compiler && $MMAKE $mmake_opts $jfactor mercury_compile)
 		then
 			echo "building of stage 2 compiler successful"
@@ -726,6 +742,12 @@
 	cp $root/browser/Mmake* $root/browser/Mercury.options .
 	$LN_S $root/browser/$BROWSER_LIB_NAME.init .
 	cd $root/stage3
+	mkdir analysis
+	cd analysis
+	$LN_S $root/analysis/*.m .
+	cp $root/analysis/Mmake* $root/analysis/Mercury.options .
+	$LN_S $root/analysis/$ANALYSIS_LIB_NAME.init .
+	cd $root/stage3
 	$LN_S $root/boehm_gc .
 	$LN_S $root/mps_gc .
 	$LN_S $root/bindist .
@@ -766,7 +788,7 @@
 	fi
 
 	if (cd stage3 && $MMAKE $mmake_opts dep_library dep_browser \
-		dep_compiler)
+		dep_analysis dep_compiler)
 	then
 		echo "building of stage 3 dependencies successful"
 	else
@@ -824,6 +846,26 @@
 		fi
 	fi
 
+	if 	(cd stage3/analysis &&
+		$MMAKE $mmake_opts $jfactor all-ints &&
+		$MMAKE $mmake_opts $jfactor ${target_ext}s)
+	then
+		echo "building of stage 3 analysis successful"
+	else
+		echo "building of stage 3 analysis initially not successful"
+		df .
+		# try again, in case the failure cause was transient
+		if 	(cd stage3/analysis &&
+			$MMAKE $mmake_opts $jfactor all-ints &&
+			$MMAKE $mmake_opts $jfactor ${target_ext}s)
+		then
+			echo "building of stage 3 analysis successful"
+		else
+			echo "building of stage 3 analysis not successful"
+			exit 1
+		fi
+	fi
+
 	if (cd stage3/compiler && $MMAKE $mmake_opts $jfactor ${target_ext}s)
 	then
 		echo "building of stage 3 compiler successful"
@@ -855,7 +897,7 @@
 		exec > "$outfile"	# redirect stdout to $outfile
 	fi
 
-	for dir in library browser compiler; do
+	for dir in library browser analysis compiler; do
 		# `mmake cs' in the compiler directory doesn't build
 		# `top_level_init.c', so we only compare the `.c'
 		# files present in the stage3 directory.
Index: util/mdemangle.c
===================================================================
RCS file: /home/mercury1/repository/mercury/util/mdemangle.c,v
retrieving revision 1.45
diff -u -u -r1.45 mdemangle.c
--- util/mdemangle.c	23 Jul 2002 19:39:00 -0000	1.45
+++ util/mdemangle.c	8 Aug 2002 15:35:52 -0000
@@ -162,8 +162,13 @@
 	static const char func[]  = "func__";
 	static const char porf[]  = "pred_or_func__";
 
+		/*
+		** XXX This is out-of-date. The compiler now generates names
+		** such as UnusedArgs__p__[1].
+		*/
 	static const char ua_suffix[] = "__ua"; /* added by unused_args.m */
 	static const char ua_suffix2[] = "__uab"; /* added by unused_args.m */
+
 	static const char ho_suffix[] = "__ho"; /* added by higher_order.m */
 
 	static const char mercury_data[] = "mercury_data_";
--------------------------------------------------------------------------
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