[m-rev.] for review: optimizations for foreign_procs

Zoltan Somogyi zs at csse.unimelb.edu.au
Thu Jan 11 18:56:29 AEDT 2007


For review by anyone.

Zoltan.

compiler/use_local_vars.m:
	Extend this optimization to handle temporaries being both defined in
	and used by foreign_proc_code instructions. This should eliminate
	unnecessary accesses to the MR_fake_reg array, and thus speed up
	programs that use foreign code a lot, including typeclass- and
	tabling-intensive programs, since those features are implemented using
	inline foreign code. I/O intensive should also benefit, but not much,
	since the cost of the I/O itself overwhelms the cost of the
	MR_fake_reg accesses.

	Group together the LLDS instructions that are handled similarly.
	Factor out some common code.

compiler/opt_util.m:
	Allow for the fact that foreign_proc_codes can now refer to
	temporaries.

compiler/opt_debug.m:
	Print more useful information about foreign_proc_code components.

compiler/prog_data.m:
	Rename the types and function symbols of the recently added
	foreign_proc attributes to avoid clashing with the keywords
	representing them in source code.

	Add a new foreign_proc attribute, proc_may_duplicate, that governs
	whether the body of foreign code is allowed to be duplicated.

compiler/table_gen.m:
	Include does_not_affect_liveness among the annotations for the
	foreign_proc calls generated by this module. Some of these procedures
	affect memory beyond their arguments, but that memory is in tables,
	not in unlisted registers.

	Allow some of the smaller code fragments generated by this module
	to be duplicated.

compiler/inlining.m:
	Respect the may_not_duplicate foreign_proc attribute.

compiler/pragma_c_gen.m:
	Transmit any annotations about liveness from the HLDS to the LLDS,
	since without does_not_affect_liveness annotations use_local_vars.m
	cannot optimize foreign_proc_codes.

	Transmit any annotations about may_duplicate from the HLDS to the LLDS,
	since with them jumpopt can do a better job.

compiler/llds.m:
	Use the new foreign_proc attribute instead of a boolean to represent
	whether a foreign code fragment may be duplicated.

compiler/jumpopt.m:
compiler/livemap.m:
compiler/proc_gen.m:
compiler/trace_gen.m:
	Conform to the changes above.

doc/reference_manual.texi:
	Document the new foreign_proc attribute.

library/array.m:
library/builtin.m:
library/char.m:
library/dir.m:
library/float.m:
library/int.m:
library/io.m:
library/lexer.m:
library/math.m:
library/private_builtin.m:
library/string.m:
library/version_array.m:
	Add does_not_affect_liveness annotations to the C foreign_procs that
	deserve them.

configure.in:
	Require the installed compiler to support does_not_affect_liveness.

cvs diff: Diffing .
Index: configure.in
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/configure.in,v
retrieving revision 1.483
diff -u -b -r1.483 configure.in
--- configure.in	9 Jan 2007 11:40:25 -0000	1.483
+++ configure.in	10 Jan 2007 04:14:28 -0000
@@ -279,7 +279,8 @@
 
 		:- pragma foreign_proc("C", return_rtti_version(Version::out),
 			[[may_call_mercury, promise_pure, terminates,
-			  ordinary_despite_detism, will_not_modify_trail]], "
+			  does_not_affect_liveness, will_not_modify_trail]],
+		"
 			Version = MR_RTTI_VERSION;
 		").
 
@@ -2192,6 +2193,7 @@
 	void *succip;
 	int global;
 	void *dummy_identity_function(void *);
+
 	foo() {
 		entry_foo_1 = && foo_1;
 		goto *dummy_identity_function(&&return_label);
cvs diff: Diffing analysis
cvs diff: Diffing bindist
cvs diff: Diffing boehm_gc
cvs diff: Diffing boehm_gc/Mac_files
cvs diff: Diffing boehm_gc/cord
cvs diff: Diffing boehm_gc/cord/private
cvs diff: Diffing boehm_gc/doc
cvs diff: Diffing boehm_gc/include
cvs diff: Diffing boehm_gc/include/private
cvs diff: Diffing boehm_gc/libatomic_ops-1.2
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/doc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/gcc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/hpc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/ibmc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/icc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/msftc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/src/atomic_ops/sysdeps/sunc
cvs diff: Diffing boehm_gc/libatomic_ops-1.2/tests
cvs diff: Diffing boehm_gc/tests
cvs diff: Diffing boehm_gc/windows-untested
cvs diff: Diffing boehm_gc/windows-untested/vc60
cvs diff: Diffing boehm_gc/windows-untested/vc70
cvs diff: Diffing boehm_gc/windows-untested/vc71
cvs diff: Diffing browser
cvs diff: Diffing bytecode
cvs diff: Diffing compiler
Index: compiler/code_info.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/code_info.m,v
retrieving revision 1.342
diff -u -b -r1.342 code_info.m
--- compiler/code_info.m	6 Jan 2007 10:56:10 -0000	1.342
+++ compiler/code_info.m	10 Jan 2007 08:16:48 -0000
@@ -1913,18 +1913,19 @@
             % inside MR_commit_{mark,cut}.
             Components = [
                 foreign_proc_raw_code(cannot_branch_away,
-                    affects_liveness, live_lvals_info(set.init),
+                    proc_affects_liveness, live_lvals_info(set.init),
                     "\t\tMR_save_transient_registers();\n"),
                 foreign_proc_raw_code(cannot_branch_away,
-                    does_not_affect_liveness, live_lvals_info(set.init),
+                    proc_does_not_affect_liveness, live_lvals_info(set.init),
                     "\t\tMR_commit_mark();\n"),
                 foreign_proc_raw_code(cannot_branch_away,
-                    affects_liveness, live_lvals_info(set.init),
+                    proc_affects_liveness, live_lvals_info(set.init),
                     "\t\tMR_restore_transient_registers();\n")
             ],
+            MD = proc_may_duplicate,
             MarkCode = node([
                 llds_instr(foreign_proc_code([], Components,
-                    proc_will_not_call_mercury, no, no, no, no, no, yes), "")
+                    proc_will_not_call_mercury, no, no, no, no, no, MD), "")
             ])
         ;
             UseMinimalModelStackCopyCut = no,
@@ -1995,12 +1996,13 @@
             % See the comment in prepare_for_semi_commit above.
             Components = [
                 foreign_proc_raw_code(cannot_branch_away,
-                    does_not_affect_liveness, live_lvals_info(set.init),
+                    proc_does_not_affect_liveness, live_lvals_info(set.init),
                     "\t\tMR_commit_cut();\n")
             ],
+            MD = proc_may_duplicate,
             CutCode = node([
                 llds_instr(foreign_proc_code([], Components,
-                    proc_will_not_call_mercury, no, no, no, no, no, yes),
+                    proc_will_not_call_mercury, no, no, no, no, no, MD),
                     "commit for temp frame hijack")
             ])
         ;
Index: compiler/inlining.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/inlining.m,v
retrieving revision 1.151
diff -u -b -r1.151 inlining.m
--- compiler/inlining.m	6 Jan 2007 09:23:35 -0000	1.151
+++ compiler/inlining.m	11 Jan 2007 07:24:48 -0000
@@ -937,8 +937,7 @@
     % whole procedures not code fragments.
     proc_info_get_eval_method(ProcInfo, eval_normal),
 
-    % Don't inline anything we have been specifically requested
-    % not to inline.
+    % Don't inline anything we have been specifically requested not to inline.
     \+ pred_info_requested_no_inlining(PredInfo),
 
     % Don't inline any procedure whose complexity we are trying to determine,
@@ -965,8 +964,7 @@
         ( Detism = detism_non ; Detism = detism_multi )
     ),
 
-    % Only inline foreign_code if it is appropriate for
-    % the target language.
+    % Only inline foreign_code if it is appropriate for the target language.
     module_info_get_globals(ModuleInfo, Globals),
     globals.get_target(Globals, Target),
     (
@@ -979,6 +977,20 @@
         ok_to_inline_language(ForeignLanguage, Target)
     ),
 
+    % Don't inline foreign_code if it is has been marked with the attribute
+    % that requests the code not be duplicated.
+    (
+        CalledGoal = hlds_goal(call_foreign_proc(ForeignAttributes,
+            _, _, _, _, _, _), _),
+        MaybeMayDuplicate = get_may_duplicate(ForeignAttributes)
+    =>
+        (
+            MaybeMayDuplicate = no
+        ;
+            MaybeMayDuplicate = yes(proc_may_duplicate)
+        )
+    ),
+
     (
         InlinePromisedPure = yes
     ;
Index: compiler/ite_gen.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/ite_gen.m,v
retrieving revision 1.97
diff -u -b -r1.97 ite_gen.m
--- compiler/ite_gen.m	6 Jan 2007 10:48:43 -0000	1.97
+++ compiler/ite_gen.m	10 Jan 2007 08:17:17 -0000
@@ -444,31 +444,32 @@
         ),
 
         PNegCondComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                live_lvals_info(set.init),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, live_lvals_info(set.init),
                 wrap_transient("\t\tMR_pneg_enter_cond();\n"))
         ],
         PNegThenComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                live_lvals_info(set.init),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, live_lvals_info(set.init),
                 wrap_transient("\t\tMR_pneg_enter_then();\n"))
         ],
         PNegElseComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                live_lvals_info(set.init),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, live_lvals_info(set.init),
                 wrap_transient("\t\tMR_pneg_enter_else(" ++ CtxtStr ++ ");\n"))
         ],
+        MD = proc_may_duplicate,
         PNegCondCode = node([
             llds_instr(foreign_proc_code([], PNegCondComponents,
-                proc_will_not_call_mercury, no, no, no, no, yes, yes), "")
+                proc_will_not_call_mercury, no, no, no, no, yes, MD), "")
         ]),
         PNegThenCode = node([
             llds_instr(foreign_proc_code([], PNegThenComponents,
-                proc_will_not_call_mercury, no, no, no, no, yes, yes), "")
+                proc_will_not_call_mercury, no, no, no, no, yes, MD), "")
         ]),
         PNegElseCode = node([
             llds_instr(foreign_proc_code([], PNegElseComponents,
-                proc_will_not_call_mercury, no, no, no, no, yes, yes), "")
+                proc_will_not_call_mercury, no, no, no, no, yes, MD), "")
         ])
     ;
         PNegCondCode = empty,
Index: compiler/jumpopt.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/jumpopt.m,v
retrieving revision 1.100
diff -u -b -r1.100 jumpopt.m
--- compiler/jumpopt.m	6 Jan 2007 09:23:36 -0000	1.100
+++ compiler/jumpopt.m	10 Jan 2007 08:32:26 -0000
@@ -67,6 +67,7 @@
 :- import_module libs.compiler_util.
 :- import_module ll_backend.code_util.
 :- import_module ll_backend.opt_util.
+:- import_module parse_tree.prog_data.
 
 :- import_module map.
 :- import_module maybe.
@@ -873,7 +874,7 @@
         % When debugging is enabled, size is in any case more important
         % than the last bit of speed.
         InstrMayBeDuplicated = no
-    ; Instr ^ fproc_maybe_dupl = no ->
+    ; Instr ^ fproc_maybe_dupl = proc_may_not_duplicate ->
         InstrMayBeDuplicated = no
     ;
         InstrMayBeDuplicated = yes
Index: compiler/livemap.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/livemap.m,v
retrieving revision 1.85
diff -u -b -r1.85 livemap.m
--- compiler/livemap.m	6 Jan 2007 10:48:43 -0000	1.85
+++ compiler/livemap.m	10 Jan 2007 05:19:17 -0000
@@ -312,10 +312,10 @@
     ;
         Component = foreign_proc_user_code(_, AffectsLiveness, Code),
         (
-            AffectsLiveness = affects_liveness,
+            AffectsLiveness = proc_affects_liveness,
             !:ContainsBadUserCode = yes
         ;
-            AffectsLiveness = default_affects_liveness,
+            AffectsLiveness = proc_default_affects_liveness,
             ( Code = "" ->
                 true
             ;
@@ -324,7 +324,7 @@
                 !:ContainsBadUserCode = yes
             )
         ;
-            AffectsLiveness = does_not_affect_liveness
+            AffectsLiveness = proc_does_not_affect_liveness
         )
     ;
         Component = foreign_proc_raw_code(_Context, AffectsLiveness,
@@ -339,16 +339,16 @@
     build_livemap_foreign_proc_components(Components,
         !Livevals, !ContainsBadUserCode).
 
-:- pred build_live_lval_info(affects_liveness::in, c_code_live_lvals::in,
+:- pred build_live_lval_info(proc_affects_liveness::in, c_code_live_lvals::in,
     string::in, lvalset::in, lvalset::out, bool::in, bool::out) is det.
 
 build_live_lval_info(AffectsLiveness, LiveLvalInfo, Code,
         !Livevals, !ContainsBadUserCode) :-
     (
-        AffectsLiveness = affects_liveness,
+        AffectsLiveness = proc_affects_liveness,
         !:ContainsBadUserCode = yes
     ;
-        AffectsLiveness = default_affects_liveness,
+        AffectsLiveness = proc_default_affects_liveness,
         ( Code = "" ->
             true
         ;
@@ -357,7 +357,7 @@
             !:ContainsBadUserCode = yes
         )
     ;
-        AffectsLiveness = does_not_affect_liveness,
+        AffectsLiveness = proc_does_not_affect_liveness,
         (
             LiveLvalInfo = no_live_lvals_info,
             !:ContainsBadUserCode = yes
Index: compiler/llds.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/llds.m,v
retrieving revision 1.345
diff -u -b -r1.345 llds.m
--- compiler/llds.m	6 Jan 2007 10:56:15 -0000	1.345
+++ compiler/llds.m	10 Jan 2007 08:14:02 -0000
@@ -303,7 +303,7 @@
             % (rval+1)th label in the list. e.g. computed_goto(2, [A, B, C, D])
             % will branch to label C.
 
-    ;       arbitrary_c_code(affects_liveness, c_code_live_lvals, string)
+    ;       arbitrary_c_code(proc_affects_liveness, c_code_live_lvals, string)
             % Do whatever is specified by the string, which can be any piece
             % of C code that does not have any non-local flow of control.
 
@@ -435,7 +435,7 @@
                 fproc_fix_onlylayout    :: maybe(label),
                 fproc_nofix             :: maybe(label),
                 fproc_stack_slot_ref    :: bool,
-                fproc_maybe_dupl        :: bool
+                fproc_maybe_dupl        :: proc_may_duplicate
             )
             % The first argument says what local variable declarations
             % are required for the following components, which in turn
@@ -583,9 +583,9 @@
 :- type foreign_proc_component
     --->    foreign_proc_inputs(list(foreign_proc_input))
     ;       foreign_proc_outputs(list(foreign_proc_output))
-    ;       foreign_proc_user_code(maybe(prog_context), affects_liveness,
+    ;       foreign_proc_user_code(maybe(prog_context), proc_affects_liveness,
                 string)
-    ;       foreign_proc_raw_code(can_branch_away, affects_liveness,
+    ;       foreign_proc_raw_code(can_branch_away, proc_affects_liveness,
                 c_code_live_lvals, string)
     ;       foreign_proc_fail_to(label)
     ;       foreign_proc_noop.
Index: compiler/opt_debug.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/opt_debug.m,v
retrieving revision 1.190
diff -u -b -r1.190 opt_debug.m
--- compiler/opt_debug.m	6 Jan 2007 10:48:43 -0000	1.190
+++ compiler/opt_debug.m	10 Jan 2007 08:37:16 -0000
@@ -802,7 +802,7 @@
             ++ dump_maybe_label("fix onlylayout:", MaybeProcLabel, MFOL)
             ++ dump_maybe_label("nofix:", MaybeProcLabel, MNF)
             ++ dump_bool_msg("stack slot ref:", SSR)
-            ++ dump_bool_msg("may duplicate:", MD)
+            ++ dump_may_duplicate(MD) ++ "\n"
             ++ ")"
     ).
 
@@ -822,6 +822,11 @@
 dump_bool_msg(Msg, no)  = Msg ++ " no\n".
 dump_bool_msg(Msg, yes) = Msg ++ " yes\n".
 
+:- func dump_may_duplicate(proc_may_duplicate) = string.
+
+dump_may_duplicate(proc_may_duplicate) = "may_duplicate".
+dump_may_duplicate(proc_may_not_duplicate) = "may_not_duplicate".
+
 :- func dump_may_use_atomic(may_use_atomic_alloc) = string.
 
 dump_may_use_atomic(may_use_atomic_alloc) = "may_use_atomic_alloc".
@@ -855,18 +860,28 @@
 dump_component(MaybeProcLabel, foreign_proc_outputs(Outputs)) =
     dump_output_components(MaybeProcLabel, Outputs).
 dump_component(_, foreign_proc_user_code(_, AL, Code)) =
-    dump_affects_liveness(AL) ++ "\n" ++ Code ++ "\n".
+    ( Code = "" ->
+        "empty user_code: " ++ dump_affects_liveness(AL) ++ "\n"
+    ;
+        "user_code: " ++ dump_affects_liveness(AL) ++ "\n" ++ Code ++ "\n"
+    ).
 dump_component(_, foreign_proc_raw_code(_, AL, _, Code)) =
-    dump_affects_liveness(AL) ++ "\n" ++ Code ++ "\n".
+    ( Code = "" ->
+        "empty raw_code: " ++ dump_affects_liveness(AL) ++ "\n"
+    ;
+        "raw_code:\n" ++ dump_affects_liveness(AL) ++ "\n" ++ Code ++ "\n"
+    ).
 dump_component(MaybeProcLabel, foreign_proc_fail_to(Label)) =
     "fail to " ++ dump_label(MaybeProcLabel, Label) ++ "\n".
 dump_component(_, foreign_proc_noop) = "".
 
-:- func dump_affects_liveness(affects_liveness) = string.
+:- func dump_affects_liveness(proc_affects_liveness) = string.
 
-dump_affects_liveness(affects_liveness) = "affects_liveness".
-dump_affects_liveness(does_not_affect_liveness) = "does_not_affect_liveness".
-dump_affects_liveness(default_affects_liveness) = "default_affects_liveness".
+dump_affects_liveness(proc_affects_liveness) = "affects_liveness".
+dump_affects_liveness(proc_does_not_affect_liveness) =  
+    "does_not_affect_liveness".
+dump_affects_liveness(proc_default_affects_liveness) =
+    "default_affects_liveness".
 
 :- func dump_input_components(maybe(proc_label), list(foreign_proc_input))
     = string.
Index: compiler/opt_util.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/opt_util.m,v
retrieving revision 1.160
diff -u -b -r1.160 opt_util.m
--- compiler/opt_util.m	6 Jan 2007 09:23:45 -0000	1.160
+++ compiler/opt_util.m	8 Jan 2007 04:43:43 -0000
@@ -1476,7 +1476,56 @@
 count_temps_instr(fork(_), !R, !F).
 count_temps_instr(join_and_continue(Lval, _), !R, !F) :-
     count_temps_lval(Lval, !R, !F).
-count_temps_instr(foreign_proc_code(_, _, _, _, _, _, _, _, _), !R, !F).
+count_temps_instr(foreign_proc_code(_, Comps, _, _, _, _, _, _, _), !R, !F) :-
+    count_temps_components(Comps, !R, !F).
+
+:- pred count_temps_components(list(foreign_proc_component)::in,
+    int::in, int::out, int::in, int::out) is det.
+
+count_temps_components([], !R, !F).
+count_temps_components([Comp | Comps], !R, !F) :-
+    count_temps_component(Comp, !R, !F),
+    count_temps_components(Comps, !R, !F).
+
+:- pred count_temps_component(foreign_proc_component::in,
+    int::in, int::out, int::in, int::out) is det.
+
+count_temps_component(Comp, !R, !F) :-
+    (
+        Comp = foreign_proc_inputs(Inputs),
+        count_temps_inputs(Inputs, !R, !F)
+    ;
+        Comp = foreign_proc_outputs(Outputs),
+        count_temps_outputs(Outputs, !R, !F)
+    ;
+        Comp = foreign_proc_user_code(_, _, _)
+    ;
+        Comp = foreign_proc_raw_code(_, _, _, _)
+    ;
+        Comp = foreign_proc_fail_to(_)
+    ;
+        Comp = foreign_proc_noop
+    ).
+
+:- pred count_temps_inputs(list(foreign_proc_input)::in,
+    int::in, int::out, int::in, int::out) is det.
+
+count_temps_inputs([], !R, !F).
+count_temps_inputs([Input | Inputs], !R, !F) :-
+    Input = foreign_proc_input(_VarName, _VarType, _IsDummy, _OrigType,
+        ArgRval, _MaybeForeignType, _BoxPolicy),
+    count_temps_rval(ArgRval, !R, !F),
+    count_temps_inputs(Inputs, !R, !F).
+
+:- pred count_temps_outputs(list(foreign_proc_output)::in,
+    int::in, int::out, int::in, int::out) is det.
+
+count_temps_outputs([], !R, !F).
+count_temps_outputs([Output | Outputs], !R, !F) :-
+    Output = foreign_proc_output(DestLval, _VarType, _IsDummy, _OrigType,
+        _VarName, _MaybeForeignType, _BoxPolicy),
+    count_temps_lval(DestLval, !R, !F),
+    count_temps_outputs(Outputs, !R, !F).
 
 :- pred count_temps_lval(lval::in, int::in, int::out, int::in, int::out)
     is det.
Index: compiler/pragma_c_gen.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/pragma_c_gen.m,v
retrieving revision 1.102
diff -u -b -r1.102 pragma_c_gen.m
--- compiler/pragma_c_gen.m	6 Jan 2007 10:48:44 -0000	1.102
+++ compiler/pragma_c_gen.m	10 Jan 2007 08:36:20 -0000
@@ -530,11 +530,11 @@
     (
         MayCallMercury = proc_will_not_call_mercury,
         SaveRegsComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), "")
+            proc_does_not_affect_liveness, live_lvals_info(set.init), "")
     ;
         MayCallMercury = proc_may_call_mercury,
         SaveRegsComp = foreign_proc_raw_code(cannot_branch_away,
-            affects_liveness, live_lvals_info(set.init),
+            proc_affects_liveness, live_lvals_info(set.init),
             "\tMR_save_registers();\n")
     ),
 
@@ -542,9 +542,9 @@
     (
         ThreadSafe = proc_thread_safe,
         ObtainLock = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), ""),
+            proc_does_not_affect_liveness, live_lvals_info(set.init), ""),
         ReleaseLock = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), "")
+            proc_does_not_affect_liveness, live_lvals_info(set.init), "")
     ;
         ThreadSafe = proc_not_thread_safe,
         module_info_pred_info(ModuleInfo, PredId, PredInfo),
@@ -553,16 +553,18 @@
         ObtainLockStr = "\tMR_OBTAIN_GLOBAL_LOCK("""
             ++ MangledName ++ """);\n",
         ObtainLock = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), ObtainLockStr),
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
+            ObtainLockStr),
         ReleaseLockStr = "\tMR_RELEASE_GLOBAL_LOCK("""
             ++ MangledName ++ """);\n",
         ReleaseLock = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), ReleaseLockStr)
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
+            ReleaseLockStr)
     ),
 
     % <The C code itself>
-    C_Code_Comp = foreign_proc_user_code(Context, default_affects_liveness,
-        C_Code),
+    AffectsLiveness = get_affects_liveness(Attributes),
+    C_Code_Comp = foreign_proc_user_code(Context, AffectsLiveness, C_Code),
 
     % <for semidet code, check of SUCCESS_INDICATOR>
     goal_info_get_determinism(GoalInfo, Detism),
@@ -576,22 +578,22 @@
             MaybeFailLabel = yes(FailLabel)
         ),
         DefSuccessComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init),
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
             "\tMR_bool " ++ foreign_proc_succ_ind_name ++ ";\n" ++
             "#undef SUCCESS_INDICATOR\n" ++
             "#define SUCCESS_INDICATOR " ++
                 foreign_proc_succ_ind_name ++ "\n"),
         UndefSuccessComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init),
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
             "#undef SUCCESS_INDICATOR\n" ++
             "#define SUCCESS_INDICATOR MR_r1\n")
     ;
         CheckSuccess_Comp = foreign_proc_noop,
         MaybeFailLabel = no,
         DefSuccessComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), ""),
+            proc_does_not_affect_liveness, live_lvals_info(set.init), ""),
         UndefSuccessComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), "")
+            proc_does_not_affect_liveness, live_lvals_info(set.init), "")
     ),
 
     % #ifndef MR_CONSERVATIVE_GC
@@ -603,7 +605,7 @@
     ;
         MayCallMercury = proc_may_call_mercury,
         RestoreRegsComp = foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init),
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
             "#ifndef MR_CONSERVATIVE_GC\n\t" ++
                 "MR_restore_registers();\n#endif\n")
     ),
@@ -635,12 +637,18 @@
         SaveRegsComp, ObtainLock, C_Code_Comp, ReleaseLock,
         CheckSuccess_Comp, RestoreRegsComp,
         OutputComp, UndefSuccessComp, ProcLabelHashUndef],
+    MaybeMayDupl = get_may_duplicate(Attributes),
+    (
+        MaybeMayDupl = yes(MayDupl)
+    ;
+        MaybeMayDupl = no,
     (
         ExtraArgs = [],
-        MaybeDupl = yes
+            MayDupl = proc_may_duplicate
     ;
         ExtraArgs = [_ | _],
-        MaybeDupl = no
+            MayDupl = proc_may_not_duplicate
+        )
     ),
     ExtraAttributes = get_extra_attributes(Attributes),
     ( list.member(refers_to_llds_stack, ExtraAttributes) ->
@@ -650,7 +658,7 @@
     ),
     PragmaCCode = node([
         llds_instr(foreign_proc_code(Decls, Components, MayCallMercury,
-            no, no, no, MaybeFailLabel, RefersToLLDSSTack, MaybeDupl),
+            no, no, no, MaybeFailLabel, RefersToLLDSSTack, MayDupl),
             "foreign_proc inclusion")
     ]),
     %
@@ -691,11 +699,11 @@
 make_proc_label_hash_define(ModuleInfo, PredId, ProcId,
         ProcLabelHashDef, ProcLabelHashUndef) :-
     ProcLabelHashDef = foreign_proc_raw_code(cannot_branch_away,
-        does_not_affect_liveness, live_lvals_info(set.init),
+        proc_does_not_affect_liveness, live_lvals_info(set.init),
         "#define\tMR_PROC_LABEL\t" ++
             make_proc_label_string(ModuleInfo, PredId, ProcId) ++ "\n"),
     ProcLabelHashUndef = foreign_proc_raw_code(cannot_branch_away,
-        does_not_affect_liveness, live_lvals_info(set.init),
+        proc_does_not_affect_liveness, live_lvals_info(set.init),
         "#undef\tMR_PROC_LABEL\n").
 
 :- func make_proc_label_string(module_info, pred_id, proc_id) = string.
@@ -730,6 +738,7 @@
         "inappropriate code model for nondet foreign_proc"),
     % Extract the may_call_mercury attribute.
     MayCallMercury = get_may_call_mercury(Attributes),
+    AffectsLiveness = get_affects_liveness(Attributes),
 
     % Generate #define MR_PROC_LABEL <procedure label> /* see note (5) */
     % and #undef MR_PROC_LABEL.
@@ -854,6 +863,7 @@
     Undef2 = "#undef\tSUCCEED_LAST\n",
     Undef3 = "#undef\tFAIL\n",
 
+    MD = proc_may_not_duplicate,
     (
         % Use the form that duplicates the common code if the programmer
         % asked for it, or if the code is small enough for its duplication
@@ -886,90 +896,102 @@
         CallComponents = [
             foreign_proc_inputs(InputDescs),
             foreign_proc_raw_code(cannot_branch_away,
-                does_not_affect_liveness, no_live_lvals_info, InitSaveStruct),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SaveRegs),
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                InitSaveStruct),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SaveRegs),
             ProcLabelDefine,
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef3),
-            foreign_proc_user_code(FirstContext, default_affects_liveness,
-                First),
-            foreign_proc_user_code(SharedContext, default_affects_liveness,
-                Shared),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef3),
+            foreign_proc_user_code(FirstContext, AffectsLiveness, First),
+            foreign_proc_user_code(SharedContext, AffectsLiveness, Shared),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                CallSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Succeed),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallLastSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                Succeed),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                CallLastSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SucceedDiscard),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef3),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SucceedDiscard),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef3),
             ProcLabelUndef
         ],
         CallBlockCode = node([
             llds_instr(foreign_proc_code(CallDecls, CallComponents,
-                MayCallMercury, no, no, no, no, yes, no),
+                MayCallMercury, no, no, no, no, yes, MD),
                 "Call and shared foreign_proc inclusion")
         ]),
 
         RetryDecls = [SaveStructDecl | OutDecls],
         RetryComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, InitSaveStruct),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SaveRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                InitSaveStruct),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SaveRegs),
             ProcLabelDefine,
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef3),
-            foreign_proc_user_code(LaterContext, default_affects_liveness,
-                Later),
-            foreign_proc_user_code(SharedContext, default_affects_liveness,
-                Shared),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetrySuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef3),
+            foreign_proc_user_code(LaterContext, AffectsLiveness, Later),
+            foreign_proc_user_code(SharedContext, AffectsLiveness, Shared),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RetrySuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Succeed),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryLastSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                Succeed),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RetryLastSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SucceedDiscard),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef3),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SucceedDiscard),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef3),
             ProcLabelUndef
         ],
         RetryBlockCode = node([
             llds_instr(foreign_proc_code(RetryDecls, RetryComponents,
-                MayCallMercury, no, no, no, no, yes, no),
+                MayCallMercury, no, no, no, no, yes, MD),
                 "Retry and shared foreign_proc inclusion")
         ]),
 
@@ -1001,134 +1023,161 @@
         CallDecls = [SaveStructDecl | Decls],
         CallComponents = [
             foreign_proc_inputs(InputDescs),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, InitSaveStruct),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SaveRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                InitSaveStruct),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SaveRegs),
             ProcLabelDefine,
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallDef3),
-            foreign_proc_user_code(FirstContext, default_affects_liveness,
-                First),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, GotoSharedLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, CallDef3),
+            foreign_proc_user_code(FirstContext, AffectsLiveness, First),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                GotoSharedLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                CallSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Succeed),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, CallLastSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                Succeed),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                CallLastSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SucceedDiscard),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef3),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SucceedDiscard),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef3),
             ProcLabelUndef
         ],
         CallBlockCode = node([
             llds_instr(foreign_proc_code(CallDecls, CallComponents,
-                MayCallMercury, yes(SharedLabel), no, no, no, yes, no),
+                MayCallMercury, yes(SharedLabel), no, no, no, yes, MD),
                 "Call foreign_proc inclusion")
         ]),
 
         RetryDecls = [SaveStructDecl | OutDecls],
         RetryComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, InitSaveStruct),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SaveRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                InitSaveStruct),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SaveRegs),
             ProcLabelDefine,
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryDef3),
-            foreign_proc_user_code(LaterContext, default_affects_liveness,
-                Later),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, GotoSharedLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetrySuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, RetryDef3),
+            foreign_proc_user_code(LaterContext, AffectsLiveness, Later),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                GotoSharedLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RetrySuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Succeed),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RetryLastSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                Succeed),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RetryLastSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SucceedDiscard),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef3),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SucceedDiscard),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef3),
             ProcLabelUndef
         ],
         RetryBlockCode = node([
             llds_instr(foreign_proc_code(RetryDecls, RetryComponents,
-                MayCallMercury, yes(SharedLabel), no, no, no, yes, no),
+                MayCallMercury, yes(SharedLabel), no, no, no, yes, MD),
                 "Retry foreign_proc inclusion")
         ]),
 
         SharedDecls = [SaveStructDecl | OutDecls],
         SharedComponents = [
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, InitSaveStruct),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SaveRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                InitSaveStruct),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SaveRegs),
             ProcLabelDefine,
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SharedDef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SharedDef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SharedDef3),
-            foreign_proc_user_code(SharedContext, default_affects_liveness,
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SharedDef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SharedDef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SharedDef3),
+            foreign_proc_user_code(SharedContext, AffectsLiveness,
                 Shared),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SharedSuccessLabel),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SharedSuccessLabel),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Succeed),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SharedLastSuccessLabel),
-            foreign_proc_raw_code(can_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, RestoreRegs),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                Succeed),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SharedLastSuccessLabel),
+            foreign_proc_raw_code(can_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                RestoreRegs),
             foreign_proc_outputs(OutputDescs),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, SucceedDiscard),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef1),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef2),
-            foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-                no_live_lvals_info, Undef3),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info,
+                SucceedDiscard),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef1),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef2),
+            foreign_proc_raw_code(cannot_branch_away,
+                proc_does_not_affect_liveness, no_live_lvals_info, Undef3),
             ProcLabelUndef
         ],
         SharedBlockCode = node([
             llds_instr(foreign_proc_code(SharedDecls, SharedComponents,
-                MayCallMercury, no, no, no, no, yes, no),
+                MayCallMercury, no, no, no, no, yes, MD),
                 "Shared foreign_proc inclusion")
         ]),
 
Index: compiler/proc_gen.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/proc_gen.m,v
retrieving revision 1.16
diff -u -b -r1.16 proc_gen.m
--- compiler/proc_gen.m	6 Jan 2007 10:48:44 -0000	1.16
+++ compiler/proc_gen.m	10 Jan 2007 08:34:35 -0000
@@ -913,13 +913,15 @@
             string.format("#define\tMR_ORDINARY_SLOTS\t%d\n",
                 [i(TotalSlots)], DefineStr),
             DefineComponents = [foreign_proc_raw_code(cannot_branch_away,
-                does_not_affect_liveness, live_lvals_info(set.init), DefineStr)],
+                proc_does_not_affect_liveness, live_lvals_info(set.init),
+                DefineStr)],
             NondetFrameInfo = ordinary_frame(PushMsg, TotalSlots, yes(Struct)),
+            MD = proc_may_not_duplicate,
             AllocCode = node([
                 llds_instr(mkframe(NondetFrameInfo, yes(OutsideResumeAddress)),
                     "Allocate stack frame"),
                 llds_instr(foreign_proc_code([], DefineComponents,
-                    proc_will_not_call_mercury, no, no, no, no, no, no), "")
+                    proc_will_not_call_mercury, no, no, no, no, no, MD), "")
             ]),
             NondetPragma = yes
         ;
@@ -1007,10 +1009,12 @@
         NondetPragma = yes,
         UndefStr = "#undef\tMR_ORDINARY_SLOTS\n",
         UndefComponents = [foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), UndefStr)],
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
+            UndefStr)],
+        MD = proc_may_not_duplicate,
         UndefCode = node([
             llds_instr(foreign_proc_code([], UndefComponents,
-                proc_will_not_call_mercury, no, no, no, no, no, no), "")
+                proc_will_not_call_mercury, no, no, no, no, no, MD), "")
         ]),
         RestoreDeallocCode = empty, % always empty for nondet code
         ExitCode = tree_list([StartComment, UndefCode, EndComment])
@@ -1169,12 +1173,13 @@
                 ReturnMacroName = "MR_tbl_mmos_return_answer",
                 ReturnCodeStr = "\t" ++ ReturnMacroName ++ "(" ++
                     DebugStr ++ ", " ++ GeneratorLocnStr ++ ");\n",
-                Component = foreign_proc_user_code(no, does_not_affect_liveness,
-                    ReturnCodeStr),
+                Component = foreign_proc_user_code(no,
+                    proc_does_not_affect_liveness, ReturnCodeStr),
+                MD = proc_may_not_duplicate,
                 SuccessCode = node([
                     llds_instr(livevals(LiveLvals), ""),
                     llds_instr(foreign_proc_code([], [Component],
-                        proc_may_call_mercury, no, no, no, no, no, no), "")
+                        proc_may_call_mercury, no, no, no, no, no, MD), "")
                 ])
             ;
                 MaybeSpecialReturn = no,
@@ -1268,20 +1273,22 @@
         ], BytecodeCall),
 
     BytecodeInstructionsComponents = [
-        foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-            live_lvals_info(set.init), "\t{\n"),
-        foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-            live_lvals_info(set.init), CallStruct),
-        foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-            no_live_lvals_info, BytecodeCall),
-        foreign_proc_raw_code(cannot_branch_away, does_not_affect_liveness,
-            live_lvals_info(set.init), "\t}\n")
+        foreign_proc_raw_code(cannot_branch_away,
+            proc_does_not_affect_liveness, live_lvals_info(set.init), "\t{\n"),
+        foreign_proc_raw_code(cannot_branch_away,
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
+            CallStruct),
+        foreign_proc_raw_code(cannot_branch_away,
+            proc_does_not_affect_liveness, no_live_lvals_info, BytecodeCall),
+        foreign_proc_raw_code(cannot_branch_away,
+            proc_does_not_affect_liveness, live_lvals_info(set.init), "\t}\n")
     ],
 
+    MD = proc_may_not_duplicate,
     BytecodeInstructions = [
         llds_instr(label(EntryLabel), "Procedure entry point"),
         llds_instr(foreign_proc_code([], BytecodeInstructionsComponents,
-            proc_may_call_mercury, no, no, no, no, no, no), "Entry stub")
+            proc_may_call_mercury, no, no, no, no, no, MD), "Entry stub")
     ].
 
 %---------------------------------------------------------------------------%
Index: compiler/prog_data.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/prog_data.m,v
retrieving revision 1.185
diff -u -b -r1.185 prog_data.m
--- compiler/prog_data.m	6 Jan 2007 10:48:44 -0000	1.185
+++ compiler/prog_data.m	10 Jan 2007 08:31:56 -0000
@@ -686,8 +686,6 @@
 :- func default_attributes(foreign_language) = pragma_foreign_proc_attributes.
 :- func get_may_call_mercury(pragma_foreign_proc_attributes) =
     proc_may_call_mercury.
-:- func get_affects_liveness(pragma_foreign_proc_attributes) =
-    affects_liveness.
 :- func get_thread_safe(pragma_foreign_proc_attributes) = proc_thread_safe.
 :- func get_purity(pragma_foreign_proc_attributes) = purity.
 :- func get_terminates(pragma_foreign_proc_attributes) = proc_terminates.
@@ -705,11 +703,15 @@
     proc_may_modify_trail.
 :- func get_may_call_mm_tabled(pragma_foreign_proc_attributes) =
     may_call_mm_tabled.
+:- func get_box_policy(pragma_foreign_proc_attributes) = box_policy.
+:- func get_affects_liveness(pragma_foreign_proc_attributes) =
+    proc_affects_liveness.
 :- func get_allocates_memory(pragma_foreign_proc_attributes) =
-    allocates_memory.
+    proc_allocates_memory.
 :- func get_registers_roots(pragma_foreign_proc_attributes) =
-    registers_roots.
-:- func get_box_policy(pragma_foreign_proc_attributes) = box_policy.
+    proc_registers_roots.
+:- func get_may_duplicate(pragma_foreign_proc_attributes) =
+    maybe(proc_may_duplicate).
 :- func get_extra_attributes(pragma_foreign_proc_attributes)
     = pragma_foreign_proc_extra_attributes.
 
@@ -717,10 +719,6 @@
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
 
-:- pred set_affects_liveness(affects_liveness::in,
-    pragma_foreign_proc_attributes::in,
-    pragma_foreign_proc_attributes::out) is det.
-
 :- pred set_thread_safe(proc_thread_safe::in,
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
@@ -765,15 +763,23 @@
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
 
-:- pred set_allocates_memory(allocates_memory::in,
+:- pred set_box_policy(box_policy::in,
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
 
-:- pred set_registers_roots(registers_roots::in,
+:- pred set_affects_liveness(proc_affects_liveness::in,
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
 
-:- pred set_box_policy(box_policy::in,
+:- pred set_allocates_memory(proc_allocates_memory::in,
+    pragma_foreign_proc_attributes::in,
+    pragma_foreign_proc_attributes::out) is det.
+
+:- pred set_registers_roots(proc_registers_roots::in,
+    pragma_foreign_proc_attributes::in,
+    pragma_foreign_proc_attributes::out) is det.
+
+:- pred set_may_duplicate(maybe(proc_may_duplicate)::in,
     pragma_foreign_proc_attributes::in,
     pragma_foreign_proc_attributes::out) is det.
 
@@ -834,22 +840,26 @@
     --->    native_if_possible
     ;       always_boxed.
 
-:- type affects_liveness
-    --->    affects_liveness
-    ;       does_not_affect_liveness
-    ;       default_affects_liveness.
-
-:- type allocates_memory
-    --->    does_not_allocate_memory
-    ;       allocates_bounded_memory
-    ;       allocates_unbounded_memory
-    ;       default_allocates_memory.
-
-:- type registers_roots
-    --->    registers_roots
-    ;       does_not_register_roots
-    ;       does_not_have_roots
-    ;       default_registers_roots.
+:- type proc_affects_liveness
+    --->    proc_affects_liveness
+    ;       proc_does_not_affect_liveness
+    ;       proc_default_affects_liveness.
+
+:- type proc_allocates_memory
+    --->    proc_does_not_allocate_memory
+    ;       proc_allocates_bounded_memory
+    ;       proc_allocates_unbounded_memory
+    ;       proc_default_allocates_memory.
+
+:- type proc_registers_roots
+    --->    proc_registers_roots
+    ;       proc_does_not_register_roots
+    ;       proc_does_not_have_roots
+    ;       proc_default_registers_roots.
+
+:- type proc_may_duplicate
+    --->    proc_may_duplicate
+    ;       proc_may_not_duplicate.
 
     % This type specifies the termination property of a procedure
     % defined using pragma c_code or pragma foreign_proc.
@@ -1704,9 +1714,10 @@
                 attr_may_modify_trail           :: proc_may_modify_trail,
                 attr_may_call_mm_tabled         :: may_call_mm_tabled,
                 attr_box_policy                 :: box_policy,
-                attr_affects_liveness           :: affects_liveness,
-                attr_allocates_memory           :: allocates_memory,
-                attr_registers_roots            :: registers_roots,
+                attr_affects_liveness           :: proc_affects_liveness,
+                attr_allocates_memory           :: proc_allocates_memory,
+                attr_registers_roots            :: proc_registers_roots,
+                attr_may_duplicate              :: maybe(proc_may_duplicate),
                 attr_extra_attributes ::
                     list(pragma_foreign_proc_extra_attribute)
             ).
@@ -1716,8 +1727,9 @@
         proc_not_tabled_for_io, purity_impure, depends_on_mercury_calls,
         no_user_annotated_sharing, default_exception_behaviour,
         no, no, proc_may_modify_trail, default_calls_mm_tabled,
-        native_if_possible, default_affects_liveness,
-        default_allocates_memory, default_registers_roots, []).
+        native_if_possible, proc_default_affects_liveness,
+        proc_default_allocates_memory, proc_default_registers_roots,
+        no, []).
 
 get_may_call_mercury(Attrs) = Attrs ^ attr_may_call_mercury.
 get_thread_safe(Attrs) = Attrs ^ attr_thread_safe.
@@ -1735,6 +1747,7 @@
 get_affects_liveness(Attrs) = Attrs ^ attr_affects_liveness.
 get_allocates_memory(Attrs) = Attrs ^ attr_allocates_memory.
 get_registers_roots(Attrs) = Attrs ^ attr_registers_roots.
+get_may_duplicate(Attrs) = Attrs ^ attr_may_duplicate.
 get_extra_attributes(Attrs) = Attrs ^ attr_extra_attributes.
 
 set_may_call_mercury(MayCallMercury, Attrs0, Attrs) :-
@@ -1769,6 +1782,8 @@
     Attrs = Attrs0 ^ attr_allocates_memory := AllocatesMemory.
 set_registers_roots(RegistersRoots, Attrs0, Attrs) :-
     Attrs = Attrs0 ^ attr_registers_roots := RegistersRoots.
+set_may_duplicate(MayDuplicate, Attrs0, Attrs) :-
+    Attrs = Attrs0 ^ attr_may_duplicate := MayDuplicate.
 
 attributes_to_strings(Attrs) = StringList :-
     % We ignore Lang because it isn't an attribute that you can put
@@ -1778,7 +1793,7 @@
         Purity, Terminates, _UserSharing, Exceptions, _LegacyBehaviour,
         OrdinaryDespiteDetism, MayModifyTrail, MayCallMM_Tabled,
         BoxPolicy, AffectsLiveness, AllocatesMemory, RegistersRoots,
-        ExtraAttributes),
+        MaybeMayDuplicate, ExtraAttributes),
     (
         MayCallMercury = proc_may_call_mercury,
         MayCallMercuryStr = "may_call_mercury"
@@ -1868,47 +1883,60 @@
         BoxPolicyStrList = ["always_boxed"]
     ),
     (
-        AffectsLiveness = affects_liveness,
+        AffectsLiveness = proc_affects_liveness,
         AffectsLivenessStrList = ["affects_liveness"]
     ;
-        AffectsLiveness = does_not_affect_liveness,
+        AffectsLiveness = proc_does_not_affect_liveness,
         AffectsLivenessStrList = ["doesnt_affect_liveness"]
     ;
-        AffectsLiveness = default_affects_liveness,
+        AffectsLiveness = proc_default_affects_liveness,
         AffectsLivenessStrList = []
     ),
     (
-        AllocatesMemory = does_not_allocate_memory,
+        AllocatesMemory = proc_does_not_allocate_memory,
         AllocatesMemoryStrList =["doesnt_allocate_memory"]
     ;
-        AllocatesMemory = allocates_bounded_memory,
+        AllocatesMemory = proc_allocates_bounded_memory,
         AllocatesMemoryStrList = ["allocates_bounded_memory"]
     ;
-        AllocatesMemory = allocates_unbounded_memory,
+        AllocatesMemory = proc_allocates_unbounded_memory,
         AllocatesMemoryStrList = ["allocates_unbounded_memory"]
     ;
-        AllocatesMemory = default_allocates_memory,
+        AllocatesMemory = proc_default_allocates_memory,
         AllocatesMemoryStrList = []
     ),
     (
-        RegistersRoots = registers_roots,
+        RegistersRoots = proc_registers_roots,
         RegistersRootsStrList = ["registers_roots"]
     ;
-        RegistersRoots = does_not_register_roots,
+        RegistersRoots = proc_does_not_register_roots,
         RegistersRootsStrList =["doesnt_register_roots"]
     ;
-        RegistersRoots = does_not_have_roots,
+        RegistersRoots = proc_does_not_have_roots,
         RegistersRootsStrList = ["doesnt_have_roots"]
     ;
-        RegistersRoots = default_registers_roots,
+        RegistersRoots = proc_default_registers_roots,
         RegistersRootsStrList = []
     ),
+    (
+        MaybeMayDuplicate = yes(MayDuplicate),
+        (
+            MayDuplicate = proc_may_duplicate,
+            MayDuplicateStrList = ["may_duplicate"]
+        ;
+            MayDuplicate = proc_may_not_duplicate,
+            MayDuplicateStrList = ["may_not_duplicate"]
+        )
+    ;
+        MaybeMayDuplicate = no,
+        MayDuplicateStrList = []
+    ),
     StringList = [MayCallMercuryStr, ThreadSafeStr, TabledForIOStr |
         PurityStrList] ++ TerminatesStrList ++ ExceptionsStrList ++
         OrdinaryDespiteDetismStrList ++ MayModifyTrailStrList ++
         MayCallMM_TabledStrList ++ BoxPolicyStrList ++
-        AffectsLivenessStrList ++
-        AllocatesMemoryStrList ++ RegistersRootsStrList ++
+        AffectsLivenessStrList ++ AllocatesMemoryStrList ++
+        RegistersRootsStrList ++ MayDuplicateStrList ++
         list.map(extra_attribute_to_string, ExtraAttributes).
 
 add_extra_attribute(NewAttribute, Attributes0,
Index: compiler/prog_io_pragma.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/prog_io_pragma.m,v
retrieving revision 1.120
diff -u -b -r1.120 prog_io_pragma.m
--- compiler/prog_io_pragma.m	6 Jan 2007 10:48:44 -0000	1.120
+++ compiler/prog_io_pragma.m	10 Jan 2007 08:35:18 -0000
@@ -1495,9 +1495,10 @@
     ;       coll_may_modify_trail(proc_may_modify_trail)
     ;       coll_may_call_mm_tabled(may_call_mm_tabled)
     ;       coll_box_policy(box_policy)
-    ;       coll_affects_liveness(affects_liveness)
-    ;       coll_allocates_memory(allocates_memory)
-    ;       coll_registers_roots(registers_roots).
+    ;       coll_affects_liveness(proc_affects_liveness)
+    ;       coll_allocates_memory(proc_allocates_memory)
+    ;       coll_registers_roots(proc_registers_roots)
+    ;       coll_may_duplicate(proc_may_duplicate).
 
 :- pred parse_pragma_foreign_proc_attributes_term(foreign_language::in,
     string::in, varset::in, term::in,
@@ -1543,20 +1544,22 @@
         coll_may_call_mercury(proc_will_not_call_mercury) -
             coll_may_call_mm_tabled(may_call_mm_tabled),
         coll_box_policy(native_if_possible) - coll_box_policy(always_boxed),
-        coll_affects_liveness(affects_liveness) -
-            coll_affects_liveness(does_not_affect_liveness),
-        coll_allocates_memory(does_not_allocate_memory) -
-            coll_allocates_memory(allocates_bounded_memory),
-        coll_allocates_memory(does_not_allocate_memory) -
-            coll_allocates_memory(allocates_unbounded_memory),
-        coll_allocates_memory(allocates_bounded_memory) -
-            coll_allocates_memory(allocates_unbounded_memory),
-        coll_registers_roots(does_not_register_roots) -
-            coll_registers_roots(registers_roots),
-        coll_registers_roots(does_not_register_roots) -
-            coll_registers_roots(does_not_have_roots),
-        coll_registers_roots(registers_roots) -
-            coll_registers_roots(does_not_have_roots)
+        coll_affects_liveness(proc_affects_liveness) -
+            coll_affects_liveness(proc_does_not_affect_liveness),
+        coll_allocates_memory(proc_does_not_allocate_memory) -
+            coll_allocates_memory(proc_allocates_bounded_memory),
+        coll_allocates_memory(proc_does_not_allocate_memory) -
+            coll_allocates_memory(proc_allocates_unbounded_memory),
+        coll_allocates_memory(proc_allocates_bounded_memory) -
+            coll_allocates_memory(proc_allocates_unbounded_memory),
+        coll_registers_roots(proc_does_not_register_roots) -
+            coll_registers_roots(proc_registers_roots),
+        coll_registers_roots(proc_does_not_register_roots) -
+            coll_registers_roots(proc_does_not_have_roots),
+        coll_registers_roots(proc_registers_roots) -
+            coll_registers_roots(proc_does_not_have_roots),
+        coll_may_duplicate(proc_may_duplicate) -
+            coll_may_duplicate(proc_may_not_duplicate)
     ],
     ( parse_pragma_foreign_proc_attributes_term0(Varset, Term, AttrList) ->
         (
@@ -1619,6 +1622,8 @@
     set_allocates_memory(AllocatesMemory, !Attrs).
 process_attribute(coll_registers_roots(RegistersRoots), !Attrs) :-
     set_registers_roots(RegistersRoots, !Attrs).
+process_attribute(coll_may_duplicate(MayDuplicate), !Attrs) :-
+    set_may_duplicate(yes(MayDuplicate), !Attrs).
 
     % Check whether all the required attributes have been set for
     % a particular language
@@ -1699,6 +1704,8 @@
         Flag = coll_allocates_memory(AllocatesMemory)
     ; parse_registers_roots(Term, RegistersRoots) ->
         Flag = coll_registers_roots(RegistersRoots)
+    ; parse_may_duplicate(Term, MayDuplicate) ->
+        Flag = coll_may_duplicate(MayDuplicate)
     ;
         fail
     ).
@@ -1746,21 +1753,21 @@
 parse_box_policy(term.functor(term.atom("always_boxed"), [], _),
     always_boxed).
 
-:- pred parse_affects_liveness(term::in, affects_liveness::out) is semidet.
+:- pred parse_affects_liveness(term::in, proc_affects_liveness::out) is semidet.
 
 parse_affects_liveness(Term, AffectsLiveness) :-
     Term = term.functor(term.atom(Functor), [], _),
     (
         Functor = "affects_liveness",
-        AffectsLiveness = affects_liveness
+        AffectsLiveness = proc_affects_liveness
     ;
         ( Functor = "doesnt_affect_liveness"
         ; Functor = "does_not_affect_liveness"
         ),
-        AffectsLiveness = does_not_affect_liveness
+        AffectsLiveness = proc_does_not_affect_liveness
     ).
 
-:- pred parse_allocates_memory(term::in, allocates_memory::out) is semidet.
+:- pred parse_allocates_memory(term::in, proc_allocates_memory::out) is semidet.
 
 parse_allocates_memory(Term, AllocatesMemory) :-
     Term = term.functor(term.atom(Functor), [], _),
@@ -1768,32 +1775,44 @@
         ( Functor = "doesnt_allocate_memory"
         ; Functor = "does_not_allocate_memory"
         ),
-        AllocatesMemory = does_not_allocate_memory
+        AllocatesMemory = proc_does_not_allocate_memory
     ;
         Functor = "allocates_bounded_memory",
-        AllocatesMemory = allocates_bounded_memory
+        AllocatesMemory = proc_allocates_bounded_memory
     ;
         Functor = "allocates_unbounded_memory",
-        AllocatesMemory = allocates_unbounded_memory
+        AllocatesMemory = proc_allocates_unbounded_memory
     ).
 
-:- pred parse_registers_roots(term::in, registers_roots::out) is semidet.
+:- pred parse_registers_roots(term::in, proc_registers_roots::out) is semidet.
 
 parse_registers_roots(Term, RegistersRoots) :-
     Term = term.functor(term.atom(Functor), [], _),
     (
         Functor = "registers_roots",
-        RegistersRoots = registers_roots
+        RegistersRoots = proc_registers_roots
     ;
         ( Functor = "doesnt_register_roots"
         ; Functor = "does_not_register_roots"
         ),
-        RegistersRoots = does_not_register_roots
+        RegistersRoots = proc_does_not_register_roots
     ;
         ( Functor = "doesnt_have_roots"
         ; Functor = "does_not_have_roots"
         ),
-        RegistersRoots = does_not_have_roots
+        RegistersRoots = proc_does_not_have_roots
+    ).
+
+:- pred parse_may_duplicate(term::in, proc_may_duplicate::out) is semidet.
+
+parse_may_duplicate(Term, RegistersRoots) :-
+    Term = term.functor(term.atom(Functor), [], _),
+    (
+        Functor = "may_duplicate",
+        RegistersRoots = proc_may_duplicate
+    ;
+        Functor = "may_not_duplicate",
+        RegistersRoots = proc_may_not_duplicate
     ).
 
 :- pred parse_tabled_for_io(term::in, proc_tabled_for_io::out) is semidet.
Index: compiler/table_gen.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/table_gen.m,v
retrieving revision 1.130
diff -u -b -r1.130 table_gen.m
--- compiler/table_gen.m	6 Jan 2007 09:23:54 -0000	1.130
+++ compiler/table_gen.m	10 Jan 2007 09:42:56 -0000
@@ -664,15 +664,15 @@
         "(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");\n",
 
     table_generate_foreign_proc(MarkInactivePredName, detism_det,
-        tabling_c_attributes, [TableTipArg], [],
+        tabling_c_attributes_dupl, [TableTipArg], [],
         MarkInactiveCode, purity_impure, [],
         ModuleInfo, Context, MarkInactiveGoal),
     table_generate_foreign_proc(MarkInactiveFailPredName, detism_failure,
-        tabling_c_attributes, [TableTipArg], [],
+        tabling_c_attributes_dupl, [TableTipArg], [],
         MarkInactiveFailCode, purity_impure, [],
         ModuleInfo, Context, MarkInactiveFailGoal),
     table_generate_foreign_proc(MarkActiveFailPredName, detism_failure,
-        tabling_c_attributes, [TableTipArg], [],
+        tabling_c_attributes_dupl, [TableTipArg], [],
         MarkActiveFailCode, purity_impure, [],
         ModuleInfo, Context, MarkActiveFailGoal),
 
@@ -947,7 +947,7 @@
         MarkAsFailedCode = MarkAsFailedMacroName ++
             "(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");",
         table_generate_foreign_proc(MarkAsFailedPredName, detism_failure,
-            tabling_c_attributes, [TableTipArg], [],
+            tabling_c_attributes_dupl, [TableTipArg], [],
             MarkAsFailedCode, purity_impure, [],
             ModuleInfo, Context, ElseGoal),
         InactiveGoalExpr = if_then_else([], RenamedOrigGoal,
@@ -1038,15 +1038,15 @@
         "(" ++ DebugArgStr ++ ", " ++ RecordVarName ++ ");\n",
 
     table_generate_foreign_proc(MarkIncompletePredName, detism_det,
-        tabling_c_attributes, [RecordArg], [],
+        tabling_c_attributes_dupl, [RecordArg], [],
         MarkIncompleteCode, purity_impure, [],
         ModuleInfo, Context, MarkIncompleteGoal),
     table_generate_foreign_proc(MarkActivePredName, detism_failure,
-        tabling_c_attributes, [RecordArg], [],
+        tabling_c_attributes_dupl, [RecordArg], [],
         MarkActiveCode, purity_impure, [],
         ModuleInfo, Context, MarkActiveGoal),
     table_generate_foreign_proc(MarkCompletePredName, detism_failure,
-        tabling_c_attributes, [RecordArg], [],
+        tabling_c_attributes_dupl, [RecordArg], [],
         MarkCompleteCode, purity_impure, [],
         ModuleInfo, Context, MarkCompleteGoal),
 
@@ -1665,9 +1665,9 @@
         native_if_possible),
     RestoreAllPredName = "table_mmos_restore_answers",
     table_generate_foreign_proc(RestoreAllPredName, detism_det,
-        tabling_c_attributes, [AnswerBlockArg], RestoreArgs, RestoreCodeStr,
-        purity_semipure, RestoreInstMapDeltaSrc, ModuleInfo, Context,
-        RestoreGoal),
+        tabling_c_attributes_no_dupl, [AnswerBlockArg],
+        RestoreArgs, RestoreCodeStr, purity_semipure, RestoreInstMapDeltaSrc,
+        ModuleInfo, Context, RestoreGoal),
 
     GoalExpr = conj(plain_conj,
         LookupSetupGoals ++ [GetNextAnswerGoal, RestoreGoal]),
@@ -1742,7 +1742,7 @@
     PickupGeneratorArg = foreign_arg(GeneratorVar,
         yes(generator_name - out_mode), generator_type, native_if_possible),
     table_generate_foreign_proc("table_mmos_pickup_inputs", detism_det,
-        tabling_c_attributes, [PickupGeneratorArg], PickupForeignArgs,
+        tabling_c_attributes_no_dupl, [PickupGeneratorArg], PickupForeignArgs,
         PickupGeneratorCode ++ PickupVarCode, purity_impure,
         PickupInstMapDeltaSrc, ModuleInfo0, Context, PickupGoal),
 
@@ -1768,7 +1768,7 @@
     CompletionArg = foreign_arg(GeneratorVar,
         yes(generator_name - in_mode), generator_type, native_if_possible),
     table_generate_foreign_proc("table_mmos_completion", detism_failure,
-        tabling_c_attributes, [CompletionArg], [],
+        tabling_c_attributes_no_dupl, [CompletionArg], [],
         CompletionCode, purity_impure,
         [], ModuleInfo0, Context, CompletionGoal),
 
@@ -1997,7 +1997,8 @@
         LookupCodeStr ++
         CallTableTipAssignStr ++
         MainPredCodeStr,
-    table_generate_foreign_proc(PredName, detism_det, tabling_c_attributes,
+    table_generate_foreign_proc(PredName, detism_det,
+        tabling_c_attributes_no_dupl,
         Args, LookupForeignArgs, CodeStr, purity_impure,
         ground_vars(BoundVars), ModuleInfo, Context, SetupGoal0),
     attach_call_table_tip(SetupGoal0, SetupGoal),
@@ -2054,7 +2055,7 @@
         cur_table_node_name ++ ", " ++ RecordVarName ++ ", " ++
         StatusVarName ++ ");\n",
     table_generate_foreign_proc(SetupPredName, detism_det,
-        tabling_c_attributes, Args, LookupForeignArgs,
+        tabling_c_attributes_no_dupl, Args, LookupForeignArgs,
         LookupDeclCodeStr ++ PredCodeStr, purity_impure,
         ground_vars(BoundVars), ModuleInfo, Context, SetupGoal0),
     attach_call_table_tip(SetupGoal0, SetupGoal),
@@ -2110,8 +2111,9 @@
     CodeStr = LookupDeclStr ++ LookupCodeStr ++ SetupCodeStr,
     ModuleInfo = !.TableInfo ^ table_module_info,
     table_generate_foreign_proc(SetupPredName, detism_det,
-        tabling_c_attributes, Args, LookupForeignArgs, CodeStr, purity_impure,
-        ground_vars(BoundVars), ModuleInfo, Context, SetupGoal0),
+        tabling_c_attributes_no_dupl, Args, LookupForeignArgs, CodeStr,
+        purity_impure, ground_vars(BoundVars), ModuleInfo, Context,
+        SetupGoal0),
     attach_call_table_tip(SetupGoal0, SetupGoal),
     LookupSetupGoals = LookupPrefixGoals ++ [SetupGoal],
 
@@ -2421,7 +2423,7 @@
             BlockSize, CreateMacroName, Context, !VarSet, !VarTypes,
             !TableInfo, SaveArgs, SavePrefixGoals, SaveDeclCode, SaveCode),
         table_generate_foreign_proc(CreatePredName, detism_det,
-            tabling_c_attributes, [TableArg], SaveArgs,
+            tabling_c_attributes_dupl, [TableArg], SaveArgs,
             SaveDeclCode ++ SaveCode, purity_impure, [],
             ModuleInfo, Context, SaveGoal),
         Goals = SavePrefixGoals ++ [SaveGoal]
@@ -2432,7 +2434,7 @@
         MarkAsSucceededCode = MarkAsSucceededMacroName ++
             "(" ++ DebugArgStr ++ ", " ++ cur_table_node_name ++ ");",
         table_generate_foreign_proc(MarkAsSucceededPredName, detism_det,
-            tabling_c_attributes, [TableArg], [],
+            tabling_c_attributes_dupl, [TableArg], [],
             MarkAsSucceededCode, purity_impure, [],
             ModuleInfo, Context, SaveGoal),
         Goals = [SaveGoal]
@@ -2490,7 +2492,7 @@
         "\tif (" ++ SuccName ++ ") {\n" ++ CreateSaveCode ++ "\t}\n" ++
         AssignSuccessCodeStr,
     table_generate_foreign_proc(DuplCheckPredName, detism_semi,
-        tabling_c_attributes, [RecordArg], LookupForeignArgs, CodeStr,
+        tabling_c_attributes_dupl, [RecordArg], LookupForeignArgs, CodeStr,
         purity_impure, [], ModuleInfo, Context, DuplicateCheckSaveGoal),
     Goals = LookupPrefixGoals ++ [DuplicateCheckSaveGoal].
 
@@ -2545,7 +2547,7 @@
         GetCodeStr ++ LookupCodeStr ++ DuplCheckCodeStr ++
         CondSaveStr ++ AssignSuccessCodeStr,
     table_generate_foreign_proc(DuplCheckPredName, detism_semi,
-        tabling_c_attributes, Args, LookupForeignArgs,
+        tabling_c_attributes_dupl, Args, LookupForeignArgs,
         CodeStr, purity_impure, [],
         ModuleInfo, Context, DuplicateCheckSaveGoal),
     Goals = LookupPrefixGoals ++ [DuplicateCheckSaveGoal].
@@ -2630,7 +2632,7 @@
         DuplCheckCodeStr ++ CondSaveCodeStr ++ AssignSuccessCodeStr,
     ModuleInfo = !.TableInfo ^ table_module_info,
     table_generate_foreign_proc(DuplCheckPredName, detism_semi,
-        tabling_c_attributes, Args, LookupForeignArgs,
+        tabling_c_attributes_dupl, Args, LookupForeignArgs,
         CodeStr, purity_impure, [],
         ModuleInfo, Context, DuplicateCheckSaveGoal),
     Goals = LookupPrefixGoals ++ SavePrefixGoals ++
@@ -2729,7 +2731,7 @@
             answer_block_name ++ ");\n" ++
             RestoreCodeStr,
         table_generate_foreign_proc(GetPredName, detism_det,
-            tabling_c_attributes, Args, RestoreArgs,
+            tabling_c_attributes_dupl, Args, RestoreArgs,
             DeclCodeStr ++ GetRestoreCodeStr, purity_semipure,
             RestoreInstMapDeltaSrc, ModuleInfo, Context, ShortcutGoal),
         Goal = ShortcutGoal
@@ -2770,9 +2772,10 @@
         answer_block_type, native_if_possible),
     Args = [Arg],
     PredName = "table_memo_non_return_all_shortcut",
-    table_generate_foreign_proc(PredName, detism_det, tabling_c_attributes,
-        Args, RestoreArgs, RestoreCodeStr, purity_semipure,
-        RestoreInstMapDeltaSrc, ModuleInfo, Context, ShortcutGoal),
+    table_generate_foreign_proc(PredName, detism_det,
+        tabling_c_attributes_no_dupl, Args, RestoreArgs, RestoreCodeStr,
+        purity_semipure, RestoreInstMapDeltaSrc, ModuleInfo, Context,
+        ShortcutGoal),
 
     GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ShortcutGoal]),
     set.list_to_set([RecordVar | OutputVars], NonLocals),
@@ -2844,7 +2847,7 @@
     Args = [Arg],
     ReturnAllPredName = "table_mm_return_all_shortcut",
     table_generate_foreign_proc(ReturnAllPredName, detism_det,
-        tabling_c_attributes, Args, RestoreArgs, RestoreCodeStr,
+        tabling_c_attributes_no_dupl, Args, RestoreArgs, RestoreCodeStr,
         purity_semipure, RestoreInstMapDeltaSrc, ModuleInfo, Context,
         ReturnAllGoal),
     GoalExpr = conj(plain_conj, [ReturnAnswerBlocksGoal, ReturnAllGoal]),
@@ -3524,11 +3527,20 @@
 
 %-----------------------------------------------------------------------------%
 
-:- func tabling_c_attributes = pragma_foreign_proc_attributes.
+:- func tabling_c_attributes_dupl = pragma_foreign_proc_attributes.
+
+tabling_c_attributes_dupl = Attrs :-
+    Attrs0 = default_attributes(lang_c),
+    set_may_call_mercury(proc_will_not_call_mercury, Attrs0, Attrs1),
+    set_affects_liveness(proc_does_not_affect_liveness, Attrs1, Attrs2),
+    set_may_duplicate(yes(proc_may_duplicate), Attrs2, Attrs).
+
+:- func tabling_c_attributes_no_dupl = pragma_foreign_proc_attributes.
 
-tabling_c_attributes = Attrs :-
+tabling_c_attributes_no_dupl = Attrs :-
     Attrs0 = default_attributes(lang_c),
-    set_may_call_mercury(proc_will_not_call_mercury, Attrs0, Attrs).
+    set_may_call_mercury(proc_will_not_call_mercury, Attrs0, Attrs1),
+    set_affects_liveness(proc_does_not_affect_liveness, Attrs1, Attrs).
 
 :- func make_generator_c_attributes = pragma_foreign_proc_attributes.
 
Index: compiler/trace_gen.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/trace_gen.m,v
retrieving revision 1.15
diff -u -b -r1.15 trace_gen.m
--- compiler/trace_gen.m	6 Jan 2007 10:56:17 -0000	1.15
+++ compiler/trace_gen.m	10 Jan 2007 08:23:45 -0000
@@ -625,10 +625,11 @@
         TraceStmt1 = FillSlotsUptoTrail
     ),
     TraceComponents1 = [foreign_proc_raw_code(cannot_branch_away,
-        does_not_affect_liveness, live_lvals_info(set.init), TraceStmt1)],
+        proc_does_not_affect_liveness, live_lvals_info(set.init), TraceStmt1)],
+    MD = proc_may_not_duplicate,
     TraceCode1 = node([
         llds_instr(foreign_proc_code([], TraceComponents1,
-            proc_will_not_call_mercury, no, no, MaybeLayoutLabel, no, yes, no),
+            proc_will_not_call_mercury, no, no, MaybeLayoutLabel, no, yes, MD),
             "")
     ]),
     (
@@ -645,10 +646,11 @@
         stackref_to_string(CallTableLval, CallTableLvalStr),
         TraceStmt3 = "\t\t" ++ CallTableLvalStr ++ " = 0;\n",
         TraceComponents3 = [foreign_proc_raw_code(cannot_branch_away,
-            does_not_affect_liveness, live_lvals_info(set.init), TraceStmt3)],
+            proc_does_not_affect_liveness, live_lvals_info(set.init),
+            TraceStmt3)],
         TraceCode3 = node([
             llds_instr(foreign_proc_code([], TraceComponents3,
-                proc_will_not_call_mercury, no, no, no, no, yes, no), "")
+                proc_will_not_call_mercury, no, no, no, no, yes, MD), "")
         ])
     ;
         MaybeCallTableLval = no,
@@ -673,7 +675,7 @@
         ),
         ResetStmt = MacroStr ++ "(" ++ CallDepthStr ++ ");\n",
         TraceCode = node([
-            llds_instr(arbitrary_c_code(does_not_affect_liveness,
+            llds_instr(arbitrary_c_code(proc_does_not_affect_liveness,
                 live_lvals_info(set.init), ResetStmt), "")
         ])
     ;
@@ -922,7 +924,9 @@
         true
     ),
     TraceComponents = [foreign_proc_raw_code(cannot_branch_away,
-        does_not_affect_liveness, live_lvals_info(LiveLvalSet), TraceStmt)],
+        proc_does_not_affect_liveness, live_lvals_info(LiveLvalSet),
+        TraceStmt)],
+    MD = proc_may_not_duplicate,
     TraceCode =
         node([
             llds_instr(label(Label),
@@ -933,7 +937,7 @@
                 % pair is preceded by another label, and this way we can
                 % eliminate this other label.
             llds_instr(foreign_proc_code([], TraceComponents,
-                proc_may_call_mercury, no, no, yes(Label), no, yes, no), "")
+                proc_may_call_mercury, no, no, yes(Label), no, yes, MD), "")
         ]),
     Code = tree(ProduceCode, TraceCode).
 
Index: compiler/use_local_vars.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/compiler/use_local_vars.m,v
retrieving revision 1.32
diff -u -b -r1.32 use_local_vars.m
--- compiler/use_local_vars.m	6 Jan 2007 09:23:58 -0000	1.32
+++ compiler/use_local_vars.m	10 Jan 2007 05:17:17 -0000
@@ -89,6 +89,7 @@
 :- import_module ll_backend.livemap.
 :- import_module ll_backend.opt_debug.
 :- import_module ll_backend.opt_util.
+:- import_module parse_tree.prog_data.
 
 :- import_module int.
 :- import_module map.
@@ -151,7 +152,7 @@
 
 use_local_vars_instrs(!RestInstrs, !TempCounter,
         NumRealRRegs, AccessThreshold, MaybeLiveMap, MaybeFallThrough) :-
-    opt_assign(!RestInstrs, !TempCounter, NumRealRRegs, MaybeLiveMap,
+    opt_assign(!RestInstrs, !TempCounter, NumRealRRegs, [], MaybeLiveMap,
         MaybeFallThrough),
     ( AccessThreshold >= 1 ->
         opt_access(!RestInstrs, !TempCounter, NumRealRRegs,
@@ -163,18 +164,30 @@
 %-----------------------------------------------------------------------------%
 
 :- pred opt_assign(list(instruction)::in, list(instruction)::out,
-    counter::in, counter::out, int::in, maybe(livemap)::in, maybe(label)::in)
-    is det.
+    counter::in, counter::out, int::in, list(lval)::in,
+    maybe(livemap)::in, maybe(label)::in) is det.
 
-opt_assign([], [], !TempCounter, _, _, _).
+opt_assign([], [], !TempCounter, _, _, _, _).
 opt_assign([Instr0 | TailInstrs0], Instrs, !TempCounter, NumRealRRegs,
-        MaybeLiveMap, MaybeFallThrough) :-
+        !.AvoidLvals, MaybeLiveMap, MaybeFallThrough) :-
     Instr0 = llds_instr(Uinstr0, _Comment0),
     (
-        ( Uinstr0 = assign(ToLval, _FromRval)
-        ; Uinstr0 = incr_hp(ToLval, _MaybeTag, _SizeRval, _MO, _Type, _Atomic)
+        (
+            (
+                Uinstr0 = assign(ToLval, _FromRval)
+            ;
+                Uinstr0 = incr_hp(ToLval, _MaybeTag, _SizeRval, _MO, _Type,
+                    _Atomic)
         ),
-        base_lval_worth_replacing(NumRealRRegs, ToLval)
+            base_lval_worth_replacing(NumRealRRegs, ToLval),
+            MaybeMore = no
+        ;
+            Uinstr0 = foreign_proc_code(_D, Comps, _MCM, _FNL, _FL, _FOL, _NF,
+                _S, _MD),
+            opt_assign_find_output_in_components(Comps, NumRealRRegs,
+                !.AvoidLvals, ToLval),
+            MaybeMore = yes
+        )
     ->
         (
             ToLval = reg(_, _),
@@ -189,10 +202,19 @@
             list.map_foldl(
                 exprn_aux.substitute_lval_in_instr(ToLval, NewLval),
                 TailInstrs0, TailInstrs1, 0, _),
+            (
+                MaybeMore = no,
             opt_assign(TailInstrs1, TailInstrs, !TempCounter, NumRealRRegs,
-                MaybeLiveMap, MaybeFallThrough),
+                    [], MaybeLiveMap, MaybeFallThrough),
             Instrs = [Instr | TailInstrs]
         ;
+                MaybeMore = yes,
+                !:AvoidLvals = [ToLval | !.AvoidLvals],
+                Instrs1 = [Instr | TailInstrs1],
+                opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
+                    !.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
+            )
+        ;
             counter.allocate(TempNum, !TempCounter),
             NewLval = temp(reg_r, TempNum),
             substitute_lval_in_instr_until_defn(ToLval, NewLval,
@@ -201,20 +223,71 @@
         ->
             substitute_lval_in_defn(ToLval, NewLval, Instr0, Instr),
             CopyInstr = llds_instr(assign(ToLval, lval(NewLval)), ""),
+            (
+                MaybeMore = no,
             opt_assign(TailInstrs1, TailInstrs, !TempCounter, NumRealRRegs,
-                MaybeLiveMap, MaybeFallThrough),
+                    [], MaybeLiveMap, MaybeFallThrough),
             Instrs = [Instr, CopyInstr | TailInstrs]
         ;
+                MaybeMore = yes,
+                !:AvoidLvals = [ToLval | !.AvoidLvals],
+                Instrs1 = [Instr, CopyInstr | TailInstrs1],
+                opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
+                    !.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
+            )
+        ;
+            (
+                MaybeMore = no,
             opt_assign(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
-                MaybeLiveMap, MaybeFallThrough),
+                    [], MaybeLiveMap, MaybeFallThrough),
             Instrs = [Instr0 | TailInstrs]
+            ;
+                MaybeMore = yes,
+                !:AvoidLvals = [ToLval | !.AvoidLvals],
+                Instrs1 = [Instr0 | TailInstrs0],
+                opt_assign(Instrs1, Instrs, !TempCounter, NumRealRRegs,
+                    !.AvoidLvals, MaybeLiveMap, MaybeFallThrough)
+            )
         )
     ;
         opt_assign(TailInstrs0, TailInstrs, !TempCounter, NumRealRRegs,
-            MaybeLiveMap, MaybeFallThrough),
+            [], MaybeLiveMap, MaybeFallThrough),
         Instrs = [Instr0 | TailInstrs]
     ).
 
+:- pred opt_assign_find_output_in_components(list(foreign_proc_component)::in,
+    int::in, list(lval)::in, lval::out) is semidet.
+
+opt_assign_find_output_in_components([Comp | Comps], NumRealRRegs, AvoidLvals,
+        ToLval) :-
+    (
+        Comp = foreign_proc_outputs(Outputs),
+        opt_assign_find_output_in_outputs(Outputs, NumRealRRegs, AvoidLvals,
+            ToLvalPrime)
+    ->
+        ToLval = ToLvalPrime
+    ;
+        opt_assign_find_output_in_components(Comps, NumRealRRegs, AvoidLvals,
+            ToLval)
+    ).
+
+:- pred opt_assign_find_output_in_outputs(list(foreign_proc_output)::in,
+    int::in, list(lval)::in, lval::out) is semidet.
+
+opt_assign_find_output_in_outputs([Output | Outputs], NumRealRRegs, AvoidLvals,
+        ToLval) :-
+    Output = foreign_proc_output(Dest, _Type, _IsDummy, _VarName,
+        _OrigType, _MaybeForeignType, _BoxPolicy),
+    (
+        base_lval_worth_replacing(NumRealRRegs, Dest),
+        not list.member(Dest, AvoidLvals)
+    ->
+        ToLval = Dest
+    ;
+        opt_assign_find_output_in_outputs(Outputs, NumRealRRegs, AvoidLvals,
+            ToLval)
+    ).
+
 %-----------------------------------------------------------------------------%
 
 :- type maybe_compulsory_lvals
@@ -409,12 +482,55 @@
         expect(unify(ToLval, OldLval),
             this_file, "substitute_lval_in_defn: mismatch in incr_hp"),
         Uinstr = incr_hp(NewLval, MaybeTag, SizeRval, MO, Type, MayUseAtomic)
+    ; Uinstr0 = foreign_proc_code(D, Comps0, MCM, FNL, FL, FOL, NF, S, MD) ->
+        substitute_lval_in_defn_components(OldLval, NewLval, Comps0, Comps,
+            0, NumSubsts),
+        expect(unify(NumSubsts, 1),
+            this_file,
+            "substitute_lval_in_defn: mismatch in foreign_proc_code"),
+        Uinstr = foreign_proc_code(D, Comps, MCM, FNL, FL, FOL, NF, S, MD)
     ;
         unexpected(this_file,
             "substitute_lval_in_defn: unexpected instruction")
     ),
     Instr = llds_instr(Uinstr, Comment).
 
+:- pred substitute_lval_in_defn_components(lval::in, lval::in,
+    list(foreign_proc_component)::in, list(foreign_proc_component)::out,
+    int::in, int::out) is det.
+
+substitute_lval_in_defn_components(_OldLval, _NewLval, [], [], !NumSubsts).
+substitute_lval_in_defn_components(OldLval, NewLval,
+        [Comp0 | Comps0], [Comp | Comps], !NumSubsts) :-
+    ( Comp0 = foreign_proc_outputs(Outputs0) ->
+        substitute_lval_in_defn_outputs(OldLval, NewLval,
+            Outputs0, Outputs, !NumSubsts),
+        Comp = foreign_proc_outputs(Outputs)
+    ;
+        Comp = Comp0
+    ),
+    substitute_lval_in_defn_components(OldLval, NewLval, Comps0, Comps,
+        !NumSubsts).
+
+:- pred substitute_lval_in_defn_outputs(lval::in, lval::in,
+    list(foreign_proc_output)::in, list(foreign_proc_output)::out,
+    int::in, int::out) is det.
+
+substitute_lval_in_defn_outputs(_OldLval, _NewLval, [], [], !NumSubsts).
+substitute_lval_in_defn_outputs(OldLval, NewLval,
+        [Output0 | Outputs0], [Output | Outputs], !NumSubsts) :-
+    Output0 = foreign_proc_output(Dest0, Type, IsDummy, VarName,
+        OrigType, MaybeForeignType, BoxPolicy),
+    ( Dest0 = OldLval ->
+        Output = foreign_proc_output(NewLval, Type, IsDummy, VarName,
+            OrigType, MaybeForeignType, BoxPolicy),
+        !:NumSubsts = !.NumSubsts + 1
+    ;
+        Output = Output0
+    ),
+    substitute_lval_in_defn_outputs(OldLval, NewLval, Outputs0, Outputs,
+        !NumSubsts).
+
     % Substitute NewLval for OldLval in an instruction sequence
     % until we come an instruction that may define OldLval.
     % We don't worry about instructions that define a variable that
@@ -457,88 +573,171 @@
 substitute_lval_in_instr_until_defn_2(OldLval, NewLval, !Instr, !Instrs, !N) :-
     !.Instr = llds_instr(Uinstr0, _),
     (
-        Uinstr0 = comment(_),
-        substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
-    ;
-        Uinstr0 = livevals(_)
-    ;
         Uinstr0 = block(_, _, _),
         unexpected(this_file,
             "substitute_lval_in_instr_until_defn: found block")
     ;
         Uinstr0 = assign(Lval, _),
-        ( Lval = OldLval ->
+        ( assignment_updates_oldlval(Lval, OldLval) = yes ->
+            % XXX we should still substitute on the rhs
             % If we alter any lval that occurs in OldLval, we must stop
-            % the substitutions. At the moment, the only lval OldLval
-            % contains is itself.
+            % the substitutions.
             true
         ;
             exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
             substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
         )
     ;
-        Uinstr0 = llcall(_, _, _, _, _, _)
-    ;
-        Uinstr0 = mkframe(_, _)
-    ;
-        Uinstr0 = label(_)
-    ;
-        Uinstr0 = goto(_)
-    ;
-        Uinstr0 = computed_goto(_, _),
-        exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N)
-    ;
-        Uinstr0 = if_val(_, _),
-        exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N)
-    ;
-        Uinstr0 = save_maxfr(_)
-    ;
-        Uinstr0 = restore_maxfr(_)
-    ;
         Uinstr0 = incr_hp(Lval, _, _, _, _, _),
-        ( Lval = OldLval ->
+        ( assignment_updates_oldlval(Lval, OldLval) = yes ->
             % If we alter any lval that occurs in OldLval, we must stop
-            % the substitutions. At the moment, the only lval OldLval
-            % contains is itself.
+            % the substitutions.
             true
         ;
             exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
             substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
         )
     ;
-        Uinstr0 = mark_hp(_)
-    ;
-        Uinstr0 = restore_hp(_)
+        Uinstr0 = foreign_proc_code(_, Components, _, _, _, _, _, _, _),
+        AffectsLiveness = components_affect_liveness(Components),
+        (
+            AffectsLiveness = no,
+            ( components_update_oldlval(Components, OldLval) = yes ->
+                % If we alter any lval that occurs in OldLval, we must stop
+                % the substitutions.
+                true
     ;
-        Uinstr0 = free_heap(_)
+                exprn_aux.substitute_lval_in_instr(OldLval, NewLval,
+                    !Instr, !N),
+                substitute_lval_in_instr_until_defn(OldLval, NewLval,
+                    !Instrs, !N)
+            )
     ;
-        Uinstr0 = store_ticket(_)
+            AffectsLiveness = yes
+        )
     ;
-        Uinstr0 = reset_ticket(_, _)
+        Uinstr0 = comment(_),
+        substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
     ;
-        Uinstr0 = discard_ticket
+        Uinstr0 = if_val(_, _),
+        exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N),
+        substitute_lval_in_instr_until_defn(OldLval, NewLval, !Instrs, !N)
     ;
-        Uinstr0 = prune_ticket
+        Uinstr0 = computed_goto(_, _),
+        exprn_aux.substitute_lval_in_instr(OldLval, NewLval, !Instr, !N)
     ;
-        Uinstr0 = mark_ticket_stack(_)
+        ( Uinstr0 = label(_)
+        ; Uinstr0 = livevals(_)
+        ; Uinstr0 = llcall(_, _, _, _, _, _)
+        ; Uinstr0 = mkframe(_, _)
+        ; Uinstr0 = goto(_)
+        ; Uinstr0 = save_maxfr(_)
+        ; Uinstr0 = restore_maxfr(_)
+        ; Uinstr0 = mark_hp(_)
+        ; Uinstr0 = restore_hp(_)
+        ; Uinstr0 = free_heap(_)
+        ; Uinstr0 = store_ticket(_)
+        ; Uinstr0 = reset_ticket(_, _)
+        ; Uinstr0 = discard_ticket
+        ; Uinstr0 = prune_ticket
+        ; Uinstr0 = mark_ticket_stack(_)
+        ; Uinstr0 = prune_tickets_to(_)
+        ; Uinstr0 = incr_sp(_, _, _)
+        ; Uinstr0 = decr_sp(_)
+        ; Uinstr0 = decr_sp_and_return(_)
+        ; Uinstr0 = init_sync_term(_, _)
+        ; Uinstr0 = fork(_)
+        ; Uinstr0 = join_and_continue(_, _)
+        ; Uinstr0 = arbitrary_c_code(_, _, _)
+        )
+    ).
+
+:- func assignment_updates_oldlval(lval, lval) = bool.
+
+assignment_updates_oldlval(Lval, OldLval) =
+    ( Lval = OldLval ->
+        % If we alter any lval that occurs in OldLval, we must stop the
+        % substitutions. At the moment, the only lval OldLval can contain
+        % is itself.
+        yes
     ;
-        Uinstr0 = prune_tickets_to(_)
+        no
+    ).
+
+:- func components_update_oldlval(list(foreign_proc_component), lval) = bool.
+
+components_update_oldlval([], _Lval) = no.
+components_update_oldlval([Component | Components], Lval) =
+    ( component_updates_oldlval(Component, Lval) = yes ->
+        yes
     ;
-        Uinstr0 = incr_sp(_, _, _)
+        components_update_oldlval(Components, Lval)
+    ).
+
+:- func component_updates_oldlval(foreign_proc_component, lval) = bool.
+
+component_updates_oldlval(Component, Lval) = Updates :-
+    (
+        Component = foreign_proc_outputs(Outputs),
+        (
+            some [Output] (
+                list.member(Output, Outputs),
+                Output ^ out_arg_dest = Lval
+            )
+        ->
+            Updates = yes
     ;
-        Uinstr0 = decr_sp(_)
+            Updates = no
+        )
     ;
-        Uinstr0 = decr_sp_and_return(_)
+        ( Component = foreign_proc_inputs(_)
+        ; Component = foreign_proc_fail_to(_)
+        ; Component = foreign_proc_noop
+        ; Component = foreign_proc_user_code(_, _, _)
+        ; Component = foreign_proc_raw_code(_, _, _, _)
+        ),
+        Updates = no
+    ).
+
+:- func components_affect_liveness(list(foreign_proc_component))
+    = bool.
+
+components_affect_liveness([]) = no.
+components_affect_liveness([Component | Components]) =
+    ( component_affects_liveness(Component) = yes ->
+        yes
     ;
-        Uinstr0 = init_sync_term(_, _)
+        components_affect_liveness(Components)
+    ).
+
+:- func component_affects_liveness(foreign_proc_component) = bool.
+
+component_affects_liveness(Component) = Affects :-
+    (
+        ( Component = foreign_proc_inputs(_)
+        ; Component = foreign_proc_outputs(_)
+        ; Component = foreign_proc_fail_to(_)
+        ; Component = foreign_proc_noop
+        ),
+        Affects = no
     ;
-        Uinstr0 = fork(_)
+        ( Component = foreign_proc_user_code(_, AffectsLiveness, Code)
+        ; Component = foreign_proc_raw_code(_, AffectsLiveness, _, Code)
+        ),
+        (
+            AffectsLiveness = proc_affects_liveness,
+            Affects = yes
     ;
-        Uinstr0 = join_and_continue(_, _)
+            AffectsLiveness = proc_does_not_affect_liveness,
+            Affects = no
     ;
-        Uinstr0 = arbitrary_c_code(_, _, _)
+            AffectsLiveness = proc_default_affects_liveness,
+            ( Code = "" ->
+                Affects = no
     ;
-        Uinstr0 = foreign_proc_code(_, _, _, _, _, _, _, _, _)
+                Affects = yes
+            )
+        )
     ).
 
 %-----------------------------------------------------------------------------%
cvs diff: Diffing compiler/notes
cvs diff: Diffing debian
cvs diff: Diffing debian/patches
cvs diff: Diffing deep_profiler
cvs diff: Diffing deep_profiler/notes
cvs diff: Diffing doc
Index: doc/reference_manual.texi
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/doc/reference_manual.texi,v
retrieving revision 1.377
diff -u -b -r1.377 reference_manual.texi
--- doc/reference_manual.texi	6 Jan 2007 10:49:04 -0000	1.377
+++ doc/reference_manual.texi	11 Jan 2007 07:10:28 -0000
@@ -6390,6 +6390,16 @@
 the compiler assumes @samp{affects_liveness},
 unless the code of the foreign_proc in question is empty.
 
+ at item @samp{may_duplicate/may_not_duplicate}
+This attribute tells the compiler
+whether it is allowed to duplicate the foreign code fragment
+through optimizations such as inlining.
+The @samp{may_duplicate} attribute says that it may;
+The @samp{may_not_duplicate} attribute says that it may not.
+In the absence of either attribute,
+the compiler is allowed make its own judgement in the matter,
+based on factors such as the size of the code fragment.
+
 @c @item
 @c @samp{does_not_allocate_memory/allocates_bounded_memory/allocates_unbounded_memory}
 @c This attribute declares whether a foreign procedure
cvs diff: Diffing extras
cvs diff: Diffing extras/base64
cvs diff: Diffing extras/cgi
cvs diff: Diffing extras/complex_numbers
cvs diff: Diffing extras/complex_numbers/samples
cvs diff: Diffing extras/complex_numbers/tests
cvs diff: Diffing extras/concurrency
cvs diff: Diffing extras/curs
cvs diff: Diffing extras/curs/samples
cvs diff: Diffing extras/curses
cvs diff: Diffing extras/curses/sample
cvs diff: Diffing extras/dynamic_linking
cvs diff: Diffing extras/error
cvs diff: Diffing extras/fixed
cvs diff: Diffing extras/gator
cvs diff: Diffing extras/gator/generations
cvs diff: Diffing extras/gator/generations/1
cvs diff: Diffing extras/graphics
cvs diff: Diffing extras/graphics/easyx
cvs diff: Diffing extras/graphics/easyx/samples
cvs diff: Diffing extras/graphics/mercury_glut
cvs diff: Diffing extras/graphics/mercury_opengl
cvs diff: Diffing extras/graphics/mercury_tcltk
cvs diff: Diffing extras/graphics/samples
cvs diff: Diffing extras/graphics/samples/calc
cvs diff: Diffing extras/graphics/samples/gears
cvs diff: Diffing extras/graphics/samples/maze
cvs diff: Diffing extras/graphics/samples/pent
cvs diff: Diffing extras/lazy_evaluation
cvs diff: Diffing extras/lex
cvs diff: Diffing extras/lex/samples
cvs diff: Diffing extras/lex/tests
cvs diff: Diffing extras/log4m
cvs diff: Diffing extras/logged_output
cvs diff: Diffing extras/moose
cvs diff: Diffing extras/moose/samples
cvs diff: Diffing extras/moose/tests
cvs diff: Diffing extras/mopenssl
cvs diff: Diffing extras/morphine
cvs diff: Diffing extras/morphine/non-regression-tests
cvs diff: Diffing extras/morphine/scripts
cvs diff: Diffing extras/morphine/source
cvs diff: Diffing extras/net
cvs diff: Diffing extras/odbc
cvs diff: Diffing extras/posix
cvs diff: Diffing extras/quickcheck
cvs diff: Diffing extras/quickcheck/tutes
cvs diff: Diffing extras/references
cvs diff: Diffing extras/references/samples
cvs diff: Diffing extras/references/tests
cvs diff: Diffing extras/solver_types
cvs diff: Diffing extras/solver_types/library
cvs diff: Diffing extras/stream
cvs diff: Diffing extras/stream/tests
cvs diff: Diffing extras/trailed_update
cvs diff: Diffing extras/trailed_update/samples
cvs diff: Diffing extras/trailed_update/tests
cvs diff: Diffing extras/windows_installer_generator
cvs diff: Diffing extras/windows_installer_generator/sample
cvs diff: Diffing extras/windows_installer_generator/sample/images
cvs diff: Diffing extras/xml
cvs diff: Diffing extras/xml/samples
cvs diff: Diffing extras/xml_stylesheets
cvs diff: Diffing java
cvs diff: Diffing java/runtime
cvs diff: Diffing library
Index: library/array.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/array.m,v
retrieving revision 1.149
diff -u -b -r1.149 array.m
--- library/array.m	23 Oct 2006 00:32:55 -0000	1.149
+++ library/array.m	10 Jan 2007 04:29:47 -0000
@@ -490,7 +490,8 @@
 
 :- pragma foreign_proc("C",
     bounds_checks,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifdef ML_OMIT_ARRAY_BOUNDS_CHECKS
     SUCCESS_INDICATOR = MR_FALSE;
@@ -578,7 +579,8 @@
 
 :- pragma foreign_proc("C",
     array.init_2(Size::in, Item::in, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ML_alloc_array(Array, Size + 1, MR_PROC_LABEL);
     ML_init_array(Array, Size, Item);
@@ -586,7 +588,8 @@
 
 :- pragma foreign_proc("C",
     array.make_empty_array(Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ML_alloc_array(Array, 1, MR_PROC_LABEL);
     ML_init_array(Array, 0, 0);
@@ -638,14 +641,16 @@
 
 :- pragma foreign_proc("C",
     array.min(Array::array_ui, Min::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     /* Array not used */
     Min = 0;
 ").
 :- pragma foreign_proc("C",
     array.min(Array::in, Min::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     /* Array not used */
     Min = 0;
@@ -683,13 +688,15 @@
 
 :- pragma foreign_proc("C",
     array.max(Array::array_ui, Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Max = Array->size - 1;
 ").
 :- pragma foreign_proc("C",
     array.max(Array::in, Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Max = Array->size - 1;
 ").
@@ -743,13 +750,15 @@
 
 :- pragma foreign_proc("C",
     array.size(Array::array_ui, Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Max = Array->size;
 ").
 :- pragma foreign_proc("C",
     array.size(Array::in, Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Max = Array->size;
 ").
@@ -842,13 +851,15 @@
 
 :- pragma foreign_proc("C",
     array.unsafe_lookup(Array::array_ui, Index::in, Item::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Item = Array->elements[Index];
 ").
 :- pragma foreign_proc("C",
     array.unsafe_lookup(Array::in, Index::in, Item::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Item = Array->elements[Index];
 ").
@@ -893,7 +904,8 @@
 
 :- pragma foreign_proc("C",
     array.unsafe_set(Array0::array_di, Index::in, Item::in, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Array0->elements[Index] = Item; /* destructive update! */
     Array = Array0;
@@ -968,7 +980,8 @@
 
 :- pragma foreign_proc("C",
     array.resize(Array0::array_di, Size::in, Item::in, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     if ((Array0)->size == Size) {
         Array = Array0;
@@ -1083,7 +1096,8 @@
 
 :- pragma foreign_proc("C",
     array.shrink_2(Array0::array_di, Size::in, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ML_alloc_array(Array, Size + 1, MR_PROC_LABEL);
     ML_shrink_array(Array, Array0, Size);
@@ -1147,7 +1161,8 @@
 
 :- pragma foreign_proc("C",
     array.copy(Array0::array_ui, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ML_alloc_array(Array, Array0->size + 1, MR_PROC_LABEL);
     ML_copy_array(Array, (MR_ConstArrayPtr) Array0);
@@ -1155,7 +1170,8 @@
 
 :- pragma foreign_proc("C",
     array.copy(Array0::in, Array::array_uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ML_alloc_array(Array, Array0->size + 1, MR_PROC_LABEL);
     ML_copy_array(Array, (MR_ConstArrayPtr) Array0);
Index: library/builtin.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/builtin.m,v
retrieving revision 1.125
diff -u -b -r1.125 builtin.m
--- library/builtin.m	23 Oct 2006 00:32:56 -0000	1.125
+++ library/builtin.m	10 Jan 2007 04:14:20 -0000
@@ -458,13 +458,15 @@
 
 :- pragma foreign_proc("C",
     cc_cast(X :: (pred(out) is cc_multi)) = (Y :: out(pred(out) is det)),
-    [will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Y = X;
 ").
 :- pragma foreign_proc("C",
     cc_cast(X :: (pred(out) is cc_nondet)) = (Y :: out(pred(out) is semidet)),
-    [will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Y = X;
 ").
@@ -509,7 +511,8 @@
 :- pragma foreign_proc("C",
     cc_cast_io(X :: (pred(out, di, uo) is cc_multi)) =
         (Y :: out(pred(out, di, uo) is det)),
-    [will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Y = X;
 ").
@@ -778,7 +781,8 @@
 
 :- pragma foreign_proc("C",
     copy(Value::ui, Copy::uo),
-    [will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     MR_save_transient_registers();
     Copy = MR_deep_copy(Value, (MR_TypeInfo) TypeInfo_for_T, NULL, NULL);
@@ -787,7 +791,8 @@
 
 :- pragma foreign_proc("C",
     copy(Value::in, Copy::uo),
-    [will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     MR_save_transient_registers();
     Copy = MR_deep_copy(Value, (MR_TypeInfo) TypeInfo_for_T, NULL, NULL);
@@ -1005,13 +1010,13 @@
 
 :- pragma foreign_proc("C",
     semidet_succeed,
-    [will_not_call_mercury, thread_safe, promise_pure],
+    [will_not_call_mercury, thread_safe, promise_pure, does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = MR_TRUE;
 ").
 :- pragma foreign_proc("C",
     semidet_fail,
-    [will_not_call_mercury, thread_safe, promise_pure],
+    [will_not_call_mercury, thread_safe, promise_pure, does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = MR_FALSE;
 ").
@@ -1056,13 +1061,13 @@
 
 :- pragma foreign_proc("C",
     cc_multi_equal(X::in, Y::out),
-    [will_not_call_mercury, thread_safe, promise_pure],
+    [will_not_call_mercury, thread_safe, promise_pure, does_not_affect_liveness],
 "
     Y = X;
 ").
 :- pragma foreign_proc("C",
     cc_multi_equal(X::di, Y::uo),
-    [will_not_call_mercury, thread_safe, promise_pure],
+    [will_not_call_mercury, thread_safe, promise_pure, does_not_affect_liveness],
 "
     Y = X;
 ").
Index: library/char.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/char.m,v
retrieving revision 1.54
diff -u -b -r1.54 char.m
--- library/char.m	19 Apr 2006 05:17:50 -0000	1.54
+++ library/char.m	10 Jan 2007 04:23:47 -0000
@@ -450,21 +450,24 @@
 
 :- pragma foreign_proc("C",
     char.to_int(Character::in, Int::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Int = (MR_UnsignedChar) Character;
 ").
 
 :- pragma foreign_proc("C",
     char.to_int(Character::in, Int::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = ((MR_UnsignedChar) Character == Int);
 ").
 
 :- pragma foreign_proc("C",
     char.to_int(Character::out, Int::in),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
     /*
     ** If the integer doesn't fit into a char, then the assignment
@@ -527,7 +530,8 @@
 
 :- pragma foreign_proc("C",
     char.max_char_value(Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
     Max = UCHAR_MAX;
 ").
Index: library/dir.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/dir.m,v
retrieving revision 1.37
diff -u -b -r1.37 dir.m
--- library/dir.m	1 Oct 2006 04:57:39 -0000	1.37
+++ library/dir.m	10 Jan 2007 04:25:18 -0000
@@ -907,7 +907,8 @@
 
 :- pragma foreign_proc("C",
     can_implement_make_directory,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #if defined(MR_WIN32)
     SUCCESS_INDICATOR = MR_TRUE;
@@ -942,7 +943,7 @@
     dir.make_single_directory_2(ErrorIfExists::in, DirName::in,
         Result::out, IO0::di, IO::uo),
     [may_call_mercury, promise_pure, tabled_for_io, thread_safe,
-        terminates, will_not_modify_trail],
+        terminates, will_not_modify_trail, does_not_affect_liveness],
 "{
 #if defined(MR_WIN32)
     if (CreateDirectory(DirName, NULL)) {
@@ -1315,7 +1316,8 @@
 can_implement_dir_foldl :- semidet_fail.
 :- pragma foreign_proc("C",
     can_implement_dir_foldl,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #if defined(MR_HAVE_OPENDIR) && defined(MR_HAVE_READDIR) && \\
         defined(MR_HAVE_CLOSEDIR)
@@ -1352,7 +1354,7 @@
 :- pragma foreign_proc("C",
     dir.open_2(DirName::in, Result::out, IO0::di, IO::uo),
     [may_call_mercury, promise_pure, tabled_for_io, thread_safe,
-        terminates, will_not_modify_trail],
+        terminates, will_not_modify_trail, does_not_affect_liveness],
 "{
 #if defined(MR_WIN32)
     WIN32_FIND_DATA file_data;
@@ -1521,7 +1523,7 @@
 :- pragma foreign_proc("C",
     copy_c_string(Ptr::in) = (Str::out),
     [will_not_call_mercury, promise_pure, thread_safe,
-        will_not_modify_trail],
+        will_not_modify_trail, does_not_affect_liveness],
 "
     MR_make_aligned_string_copy(Str, (char *) Ptr);
 ").
@@ -1558,7 +1560,7 @@
 :- pragma foreign_proc("C",
     dir.close_2(Dir::in, Status::out, Error::out, IO0::di, IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
-        will_not_modify_trail],
+        will_not_modify_trail, does_not_affect_liveness],
 "{
     IO = IO0;
 #if defined(MR_WIN32)
@@ -1625,7 +1627,7 @@
     dir.read_entry_2(Dir::in, Status::out, Error::out, FileName::out,
         IO0::di, IO::uo),
     [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
-        will_not_modify_trail],
+        will_not_modify_trail, does_not_affect_liveness],
 "{
 #if defined(MR_WIN32)
     WIN32_FIND_DATA file_data;
Index: library/float.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/float.m,v
retrieving revision 1.70
diff -u -b -r1.70 float.m
--- library/float.m	27 Dec 2006 03:18:03 -0000	1.70
+++ library/float.m	10 Jan 2007 04:19:27 -0000
@@ -256,7 +256,8 @@
 
 :- pragma foreign_proc("C",
 	float_domain_checks,
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifdef ML_OMIT_MATH_DOMAIN_CHECKS
 	SUCCESS_INDICATOR = MR_FALSE;
@@ -293,7 +294,8 @@
 
 :- pragma foreign_proc("C",
 	float(IntVal::in) = (FloatVal::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	FloatVal = IntVal;
 ").
@@ -314,7 +316,8 @@
 
 :- pragma foreign_proc("C",
 	float.ceiling_to_int(X :: in) = (Ceil :: out),
-	[will_not_call_mercury, promise_pure, thread_safe],
+	[will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
 	Ceil = (MR_Integer) ceil(X);
 ").
@@ -340,7 +343,8 @@
 
 :- pragma foreign_proc("C",
 	float.floor_to_int(X :: in) = (Floor :: out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Floor = (MR_Integer) floor(X);
 ").
@@ -366,7 +370,8 @@
 
 :- pragma foreign_proc("C",
 	float.round_to_int(X :: in) = (Round :: out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Round = (MR_Integer) floor(X + 0.5);
 ").
@@ -392,7 +397,8 @@
 
 :- pragma foreign_proc("C",
 	float.truncate_to_int(X :: in) = (Trunc :: out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Trunc = (MR_Integer) X;
 ").
@@ -499,7 +505,8 @@
 	% non-negative, as this condition is not guaranteed by either API.
 :- pragma foreign_proc("C",
 	float.hash(F::in) = (H::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	H = MR_hash_float(F);
 ").
@@ -535,7 +542,8 @@
 
 :- pragma foreign_proc("C",
 	is_nan(Flt::in),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	SUCCESS_INDICATOR = MR_is_nan(Flt);
 ").
@@ -556,7 +564,8 @@
 
 :- pragma foreign_proc("C",
 	is_inf(Flt::in),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	SUCCESS_INDICATOR = MR_is_inf(Flt);
 ").
@@ -615,7 +624,8 @@
 
 :- pragma foreign_proc("C",
 	float.max = (Max::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Max = ML_FLOAT_MAX;
 ").
@@ -634,7 +644,8 @@
 
 :- pragma foreign_proc("C",
 	float.min = (Min::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Min = ML_FLOAT_MIN;
 ").
@@ -655,7 +666,8 @@
 
 :- pragma foreign_proc("C",
 	float.epsilon = (Eps::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Eps = ML_FLOAT_EPSILON;
 ").
@@ -677,7 +689,8 @@
 
 :- pragma foreign_proc("C",
 	float.radix = (Radix::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	Radix = ML_FLOAT_RADIX;
 ").
@@ -695,7 +708,8 @@
 
 :- pragma foreign_proc("C",
 	float.mantissa_digits = (MantDig::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	MantDig = ML_FLOAT_MANT_DIG;
 ").
@@ -711,7 +725,8 @@
 
 :- pragma foreign_proc("C",
 	float.min_exponent = (MinExp::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	MinExp = ML_FLOAT_MIN_EXP;
 ").
@@ -727,7 +742,8 @@
 
 :- pragma foreign_proc("C",
 	float.max_exponent = (MaxExp::out),
-	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+	[will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 	MaxExp = ML_FLOAT_MAX_EXP;
 ").
Index: library/int.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/int.m,v
retrieving revision 1.118
diff -u -b -r1.118 int.m
--- library/int.m	27 Dec 2006 03:18:03 -0000	1.118
+++ library/int.m	10 Jan 2007 04:17:31 -0000
@@ -433,7 +433,8 @@
 
 :- pragma foreign_proc("C",
     int_domain_checks,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifdef ML_OMIT_MATH_DOMAIN_CHECKS
     SUCCESS_INDICATOR = MR_FALSE;
@@ -611,7 +612,8 @@
 
 :- pragma foreign_proc("C",
     int.max_int(Max::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     if (sizeof(MR_Integer) == sizeof(int)) {
         Max = INT_MAX;
@@ -624,7 +626,8 @@
 
 :- pragma foreign_proc("C",
     int.min_int(Min::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     if (sizeof(MR_Integer) == sizeof(int)) {
         Min = INT_MIN;
@@ -637,28 +640,32 @@
 
 :- pragma foreign_proc("C",
     int.bits_per_int(Bits::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Bits = ML_BITS_PER_INT;
 ").
 
 :- pragma foreign_proc("C",
     int.quot_bits_per_int(Int::in) = (Div::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Div = Int / ML_BITS_PER_INT;
 ").
 
 :- pragma foreign_proc("C",
     int.times_bits_per_int(Int::in) = (Result::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Result = Int * ML_BITS_PER_INT;
 ").
 
 :- pragma foreign_proc("C",
     int.rem_bits_per_int(Int::in) = (Rem::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Rem = Int % ML_BITS_PER_INT;
 ").
Index: library/io.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/io.m,v
retrieving revision 1.366
diff -u -b -r1.366 io.m
--- library/io.m	5 Jan 2007 01:14:06 -0000	1.366
+++ library/io.m	10 Jan 2007 04:14:20 -0000
@@ -1881,7 +1881,8 @@
 :- pragma foreign_proc("C",
     io.read_line_as_string_2(Stream::in, _Bool::in, Res :: out,
         RetString::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
 #define ML_IO_READ_LINE_GROW(n) ((n) * 3 / 2)
 #define ML_IO_BYTES_TO_WORDS(n) (((n) + sizeof(MR_Word) - 1) / sizeof(MR_Word))
@@ -2149,7 +2150,8 @@
 
 :- pragma foreign_proc("C",
     io.clear_err(Stream::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     if (MR_IS_FILE_STREAM(*Stream)) {
         clearerr(MR_file(*Stream));
@@ -2197,7 +2199,8 @@
 
 :- pragma foreign_proc("C",
     ferror(Stream::in, RetVal::out, RetStr::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     if (MR_IS_FILE_STREAM(*Stream)) {
         RetVal = ferror(MR_file(*Stream));
@@ -2236,7 +2239,8 @@
 
 :- pragma foreign_proc("C",
     io.get_system_error(Error::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     Error = errno;
     MR_update_io(IO0, IO);
@@ -2261,7 +2265,8 @@
 
 :- pragma foreign_proc("C",
     make_err_msg(Error::in, Msg0::in, Msg::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     ML_maybe_make_err_msg(MR_TRUE, Error, Msg0, MR_PROC_LABEL, Msg);
     MR_update_io(IO0, IO);
@@ -2289,7 +2294,8 @@
 
 :- pragma foreign_proc("C",
     have_win32,
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
 #ifdef MR_WIN32
     SUCCESS_INDICATOR = MR_TRUE;
@@ -2302,7 +2308,8 @@
 
 :- pragma foreign_proc("C",
     have_cygwin,
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
 #ifdef __CYGWIN__
     SUCCESS_INDICATOR = MR_TRUE;
@@ -2333,7 +2340,8 @@
 
 :- pragma foreign_proc("C",
     make_win32_err_msg(Error::in, Msg0::in, Msg::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     ML_maybe_make_win32_err_msg(MR_TRUE, Error, Msg0, MR_PROC_LABEL, Msg);
     MR_update_io(IO0, IO);
@@ -2379,7 +2387,8 @@
 
 :- pragma foreign_proc("C",
     io.stream_file_size(Stream::in, Size::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
 #if defined(MR_HAVE_FSTAT) && \
         (defined(MR_HAVE_FILENO) || defined(fileno)) && defined(S_ISREG)
@@ -2431,7 +2440,8 @@
 :- pragma foreign_proc("C",
     io.file_modification_time_2(FileName::in, Status::out, Msg::out,
         Time::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
 #ifdef MR_HAVE_STAT
     struct stat s;
@@ -2508,7 +2518,8 @@
 
 :- pragma foreign_proc("C",
     file_type_implemented,
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
 #ifdef MR_HAVE_STAT
     SUCCESS_INDICATOR = MR_TRUE;
@@ -2534,7 +2545,8 @@
 :- pragma foreign_proc("C",
     io.file_type_2(FollowSymLinks::in, FileName::in, Result::out,
         IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
 #ifdef MR_HAVE_STAT
     struct stat s;
@@ -2776,9 +2788,10 @@
     io.res::out, io::di, io::uo) is det.
 
 :- pragma foreign_proc("C",
-    io.check_file_accessibility_2(FileName::in, AccessTypes::in,
-        Result::out, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    io.check_file_accessibility_2(FileName::in, AccessTypes::in, Result::out,
+        IO0::di, IO::uo),
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
 #if defined(MR_HAVE_ACCESS)
   #ifdef F_OK
@@ -3114,7 +3127,7 @@
 
 :- pragma foreign_proc("C",
     compare_file_id_2(Res::out, FileId1::in, FileId2::in),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 "
     int device_cmp;
     int inode_cmp;
@@ -3172,7 +3185,8 @@
 :- pragma foreign_proc("C",
     io.file_id_2(FileName::in, Status::out, Msg::out,
         FileId::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
 #ifdef MR_HAVE_STAT
     struct stat s;
@@ -3210,7 +3224,8 @@
 have_file_ids :- semidet_fail.
 :- pragma foreign_proc("C",
     have_file_ids,
-    [promise_pure, will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [promise_pure, will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #if defined(MR_BROKEN_STAT_ST_INO) || !defined(MR_HAVE_STAT)
     /* Win32 returns junk in the st_ino field of `struct stat'. */
@@ -3243,7 +3258,8 @@
 
 :- pragma foreign_proc("C",
     io.alloc_buffer(Size::in, Buffer::buffer_uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     MR_Word buf;
     MR_offset_incr_hp_atomic_msg(buf, 0,
@@ -3262,7 +3278,8 @@
 :- pragma foreign_proc("C",
     io.resize_buffer(OldSize::in, NewSize::in,
         Buffer0::buffer_di, Buffer::buffer_uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     MR_CHECK_EXPR_TYPE(Buffer0, MR_Char *);
     MR_CHECK_EXPR_TYPE(Buffer, MR_Char *);
@@ -3303,7 +3320,8 @@
 
 :- pragma foreign_proc("C",
     io.buffer_to_string(Buffer::buffer_di, Len::in, Str::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     Str = Buffer;
     Str[Len] = '\\0';
@@ -3318,7 +3336,8 @@
 :- pragma foreign_proc("C",
     io.read_into_buffer(Stream::in, Buffer0::buffer_di, Buffer::buffer_uo,
         Pos0::in, Pos::out, Size::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     int items_read;
 
@@ -3690,7 +3709,7 @@
 
 :- pragma foreign_proc("C",
     should_reduce_stack_usage(ShouldReduce::out),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
 #ifdef  MR_EXEC_TRACE
     ShouldReduce = MR_TRUE;
@@ -4219,7 +4238,8 @@
 
 :- pragma foreign_proc("C",
     io.get_stream_db(StreamDb::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     StreamDb = ML_io_stream_db;
     MR_update_io(IO0, IO);
@@ -4229,7 +4249,8 @@
 
 :- pragma foreign_proc("C",
     io.set_stream_db(StreamDb::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     ML_io_stream_db = StreamDb;
     MR_update_io(IO0, IO);
@@ -4311,7 +4332,8 @@
 
 :- pragma foreign_proc("C",
     io.may_delete_stream_info(MayDelete::out, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     MayDelete = !MR_debug_ever_enabled;
     IO = IO0;
@@ -4364,7 +4386,8 @@
 :- pred io.lock_globals(io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.lock_globals(IO0::di, IO::uo),
-    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io],
+    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
+        does_not_affect_liveness],
 "
     #ifdef MR_THREAD_SAFE
         MR_LOCK(&ML_io_user_globals_lock, \"io.lock_globals/2\");
@@ -4380,7 +4403,8 @@
 :- pred io.unlock_globals(io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.unlock_globals(IO0::di, IO::uo),
-    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io],
+    [promise_pure, will_not_call_mercury, thread_safe, tabled_for_io,
+        does_not_affect_liveness],
 "
     #ifdef MR_THREAD_SAFE
         MR_UNLOCK(&ML_io_user_globals_lock, \"io.unlock_globals/2\");
@@ -4396,7 +4420,8 @@
 :- impure pred io.unlock_globals is det.
 :- pragma foreign_proc("C",
     io.unlock_globals,
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe,
+        does_not_affect_liveness],
 "
     #ifdef MR_THREAD_SAFE
         MR_UNLOCK(&ML_io_user_globals_lock, \"io.unlock_globals/2\");
@@ -4415,7 +4440,8 @@
 :- pred io.unsafe_get_globals(univ::uo, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.unsafe_get_globals(Globals::uo, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Globals = ML_io_user_globals;
     MR_update_io(IO0, IO);
@@ -4424,7 +4450,8 @@
 :- pred io.unsafe_set_globals(univ::di, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.unsafe_set_globals(Globals::di, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     /* XXX need to globalize the memory */
     ML_io_user_globals = Globals;
@@ -4467,7 +4494,7 @@
 
 :- pragma foreign_proc("C",
     io.get_stream_id(Stream::in) = (Id::out),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
 #ifndef MR_NATIVE_GC
     /*
@@ -4590,7 +4617,8 @@
 
 :- pragma foreign_proc("C",
     io.gc_init(StreamDbType::in, UserGlobalsType::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     /* for Windows DLLs, we need to call GC_INIT() from each DLL */
 #ifdef MR_CONSERVATIVE_GC
@@ -6063,7 +6091,8 @@
 
 :- pragma foreign_proc("C",
     io.read_char_code_2(Stream::in, CharCode::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     CharCode = mercury_getc(Stream);
     MR_update_io(IO0, IO);
@@ -6076,7 +6105,8 @@
     is det.
 :- pragma foreign_proc("C",
     io.read_byte_val_2(Stream::in, ByteVal::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     ByteVal = mercury_getc(Stream);
     MR_update_io(IO0, IO);
@@ -6085,11 +6115,11 @@
 io.putback_char(input_stream(Stream), Character, !IO) :-
     io.putback_char_2(Stream, Character, !IO).
 
-:- pred io.putback_char_2(io.stream::in, char::in, io::di, io::uo)
-    is det.
+:- pred io.putback_char_2(io.stream::in, char::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.putback_char_2(Stream::in, Character::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, terminates,
+        does_not_affect_liveness],
 "
     MercuryFilePtr mf = Stream;
     if (Character == '\\n') {
@@ -6108,7 +6138,8 @@
 :- pred io.putback_byte_2(io.stream::in, int::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.putback_byte_2(Stream::in, Character::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, terminates,
+        does_not_affect_liveness],
 "
     MercuryFilePtr mf = Stream;
     /* XXX should work even if ungetc() fails */
@@ -6194,7 +6225,8 @@
 
 :- pragma foreign_proc("C",
     io.write_string(Message::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     mercury_print_string(mercury_current_text_output, Message);
     MR_update_io(IO0, IO);
@@ -6202,7 +6234,8 @@
 
 :- pragma foreign_proc("C",
     io.write_char(Character::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_PUTCH(*mercury_current_text_output, Character) < 0) {
         mercury_output_error(mercury_current_text_output);
@@ -6215,7 +6248,8 @@
 
 :- pragma foreign_proc("C",
     io.write_int(Val::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (ML_fprintf(mercury_current_text_output, ""%ld"", (long) Val) < 0) {
         mercury_output_error(mercury_current_text_output);
@@ -6225,7 +6259,8 @@
 
 :- pragma foreign_proc("C",
     io.write_float(Val::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     char buf[MR_SPRINTF_FLOAT_BUF_SIZE];
     MR_sprintf_float(buf, Val);
@@ -6237,7 +6272,8 @@
 
 :- pragma foreign_proc("C",
     io.write_byte(Byte::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     /* call putc with a strictly non-negative byte-sized integer */
     if (MR_PUTCH(*mercury_current_binary_output,
@@ -6250,7 +6286,8 @@
 
 :- pragma foreign_proc("C",
     io.write_bytes(Message::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "{
     mercury_print_binary_string(mercury_current_binary_output, Message);
     MR_update_io(IO0, IO);
@@ -6258,7 +6295,8 @@
 
 :- pragma foreign_proc("C",
     io.flush_output(IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_FLUSH(*mercury_current_text_output) < 0) {
         mercury_output_error(mercury_current_text_output);
@@ -6268,8 +6306,8 @@
 
 :- pragma foreign_proc("C",
     io.flush_binary_output(IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe,
-        terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_FLUSH(*mercury_current_binary_output) < 0) {
         mercury_output_error(mercury_current_binary_output);
@@ -6444,7 +6482,8 @@
 
 :- pragma foreign_proc("C",
     io.seek_binary_2(Stream::in, Flag::in, Off::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     static const int seek_flags[] = { SEEK_SET, SEEK_CUR, SEEK_END };
 
@@ -6472,7 +6511,8 @@
     io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.binary_stream_offset_2(Stream::in, Offset::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     /* XXX should check for failure */
     /* XXX should check if the stream is tellable */
@@ -6496,7 +6536,8 @@
 :- pred io.write_string_2(io.stream::in, string::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.write_string_2(Stream::in, Message::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     mercury_print_string(Stream, Message);
     MR_update_io(IO0, IO);
@@ -6508,7 +6549,8 @@
 :- pred io.write_char_2(io.stream::in, char::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.write_char_2(Stream::in, Character::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_PUTCH(*Stream, Character) < 0) {
         mercury_output_error(Stream);
@@ -6525,7 +6567,8 @@
 :- pred io.write_int_2(io.stream::in, int::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.write_int_2(Stream::in, Val::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (ML_fprintf(Stream, ""%ld"", (long) Val) < 0) {
         mercury_output_error(Stream);
@@ -6539,7 +6582,8 @@
 :- pred io.write_float_2(io.stream::in, float::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.write_float_2(Stream::in, Val::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     char buf[MR_SPRINTF_FLOAT_BUF_SIZE];
     MR_sprintf_float(buf, Val);
@@ -6555,7 +6599,8 @@
 :- pred io.write_byte_2(io.stream::in, int::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.write_byte_2(Stream::in, Byte::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     /* call putc with a strictly non-negative byte-sized integer */
     if (MR_PUTCH(*Stream, (int) ((unsigned char) Byte)) < 0) {
@@ -6570,7 +6615,8 @@
 :- pred io.write_bytes_2(io.stream::in, string::in, io::di, io::uo) is det. 
 :- pragma foreign_proc("C",
     io.write_bytes_2(Stream::in, Message::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     mercury_print_binary_string(Stream, Message);
     MR_update_io(IO0, IO);
@@ -6582,7 +6628,8 @@
 :- pred io.flush_output_2(io.stream::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.flush_output_2(Stream::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_FLUSH(*Stream) < 0) {
         mercury_output_error(Stream);
@@ -6596,7 +6643,8 @@
 :- pred io.flush_binary_output_2(io.stream::in, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.flush_binary_output_2(Stream::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     if (MR_FLUSH(*Stream) < 0) {
         mercury_output_error(Stream);
@@ -6765,7 +6813,7 @@
 :- func io.stdin_stream_2 = io.stream.
 :- pragma foreign_proc("C",
     io.stdin_stream_2 = (Stream::out),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 "
     Stream = &mercury_stdin;
 ").
@@ -6776,7 +6824,8 @@
 :- pred io.stdin_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.stdin_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = &mercury_stdin;
     MR_update_io(IO0, IO);
@@ -6787,7 +6836,7 @@
 :- func io.stdout_stream_2 = io.stream.
 :- pragma foreign_proc("C",
     io.stdout_stream_2 = (Stream::out),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 "
     Stream = &mercury_stdout;
 ").
@@ -6809,7 +6858,7 @@
 :- func io.stderr_stream_2 = io.stream.
 :- pragma foreign_proc("C",
     io.stderr_stream_2 = (Stream::out),
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 "
     Stream = &mercury_stderr;
 ").
@@ -6820,7 +6869,8 @@
 :- pred io.stderr_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.stderr_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = &mercury_stderr;
     MR_update_io(IO0, IO);
@@ -6832,7 +6882,8 @@
 :- pred io.stdin_binary_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.stdin_binary_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = &mercury_stdin_binary;
     MR_update_io(IO0, IO);
@@ -6844,7 +6895,8 @@
 :- pred io.stdout_binary_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.stdout_binary_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = &mercury_stdout_binary;
     MR_update_io(IO0, IO);
@@ -6856,7 +6908,8 @@
 :- pred io.input_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.input_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     Stream = mercury_current_text_input;
     MR_update_io(IO0, IO);
@@ -6868,7 +6921,8 @@
 :- pred io.output_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.output_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     Stream = mercury_current_text_output;
     MR_update_io(IO0, IO);
@@ -6880,7 +6934,8 @@
 :- pred io.binary_input_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.binary_input_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     Stream = mercury_current_binary_input;
     MR_update_io(IO0, IO);
@@ -6892,7 +6947,8 @@
 :- pred io.binary_output_stream_2(io.stream::out, io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.binary_output_stream_2(Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     Stream = mercury_current_binary_output;
     MR_update_io(IO0, IO);
@@ -6900,7 +6956,8 @@
 
 :- pragma foreign_proc("C",
     io.get_line_number(LineNum::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     LineNum = MR_line_number(*mercury_current_text_input);
     MR_update_io(IO0, IO);
@@ -6913,7 +6970,8 @@
     is det.
 :- pragma foreign_proc("C",
     io.get_line_number_2(Stream::in, LineNum::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     LineNum = MR_line_number(*Stream);
     MR_update_io(IO0, IO);
@@ -6921,7 +6979,8 @@
 
 :- pragma foreign_proc("C",
     io.set_line_number(LineNum::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     MR_line_number(*mercury_current_text_input) = LineNum;
     MR_update_io(IO0, IO);
@@ -6935,7 +6994,8 @@
 
 :- pragma foreign_proc("C",
     io.set_line_number_2(Stream::in, LineNum::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     MR_line_number(*Stream) = LineNum;
     MR_update_io(IO0, IO);
@@ -6943,7 +7003,8 @@
 
 :- pragma foreign_proc("C",
     io.get_output_line_number(LineNum::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     LineNum = MR_line_number(*mercury_current_text_output);
     MR_update_io(IO0, IO);
@@ -6957,7 +7018,8 @@
 
 :- pragma foreign_proc("C",
     io.get_output_line_number_2(Stream::in, LineNum::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     LineNum = MR_line_number(*Stream);
     MR_update_io(IO0, IO);
@@ -6965,7 +7027,8 @@
 
 :- pragma foreign_proc("C",
     io.set_output_line_number(LineNum::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     MR_line_number(*mercury_current_text_output) = LineNum;
     MR_update_io(IO0, IO);
@@ -6978,7 +7041,8 @@
     io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.set_output_line_number_2(Stream::in, LineNum::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     MR_line_number(*Stream) = LineNum;
     MR_update_io(IO0, IO);
@@ -6991,7 +7055,8 @@
     io::di, io::uo) is det.
 :- pragma foreign_proc("C",
     io.set_input_stream_2(NewStream::in, OutStream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     OutStream = mercury_current_text_input;
     mercury_current_text_input = NewStream;
@@ -7007,7 +7072,8 @@
 
 :- pragma foreign_proc("C",
     io.set_output_stream_2(NewStream::in, OutStream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     OutStream = mercury_current_text_output;
     mercury_current_text_output = NewStream;
@@ -7023,7 +7089,8 @@
 :- pragma foreign_proc("C",
     io.set_binary_input_stream_2(NewStream::in, OutStream::out,
         IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     OutStream = mercury_current_binary_input;
     mercury_current_binary_input = NewStream;
@@ -7039,7 +7106,8 @@
 :- pragma foreign_proc("C",
     io.set_binary_output_stream_2(NewStream::in, OutStream::out,
         IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     OutStream = mercury_current_binary_output;
     mercury_current_binary_output = NewStream;
@@ -7376,7 +7444,8 @@
 :- pragma foreign_proc("C",
     io.do_open_text(FileName::in, Mode::in, ResultCode::out,
         StreamId::out, Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = mercury_open(FileName, Mode);
     ResultCode = (Stream != NULL ? 0 : -1);
@@ -7387,7 +7456,8 @@
 :- pragma foreign_proc("C",
     io.do_open_binary(FileName::in, Mode::in, ResultCode::out,
         StreamId::out, Stream::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     Stream = mercury_open(FileName, Mode);
     ResultCode = (Stream != NULL ? 0 : -1);
@@ -7495,7 +7565,8 @@
 
 :- pragma foreign_proc("C",
     io.close_stream(Stream::in, IO0::di, IO::uo),
-    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates],
+    [may_call_mercury, promise_pure, tabled_for_io, thread_safe, terminates,
+        does_not_affect_liveness],
 "
     mercury_close(Stream);
     MR_update_io(IO0, IO);
@@ -7519,7 +7590,8 @@
 
 :- pragma foreign_proc("C",
     io.progname(DefaultProgname::in, PrognameOut::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "
     if (MR_progname) {
         MR_make_aligned_string(PrognameOut, MR_progname);
@@ -7531,7 +7603,8 @@
 
 :- pragma foreign_proc("C",
     io.command_line_arguments(Args::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     int i;
 
@@ -7547,7 +7620,8 @@
 
 :- pragma foreign_proc("C",
     io.get_exit_status(ExitStatus::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     ExitStatus = mercury_exit_status;
     MR_update_io(IO0, IO);
@@ -7555,7 +7629,8 @@
 
 :- pragma foreign_proc("C",
     io.set_exit_status(ExitStatus::in, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     mercury_exit_status = ExitStatus;
     MR_update_io(IO0, IO);
@@ -7564,7 +7639,8 @@
 :- pragma foreign_proc("C",
     io.call_system_code(Command::in, Status::out, Msg::out,
         IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io],
+    [will_not_call_mercury, promise_pure, tabled_for_io,
+        does_not_affect_liveness],
 "
     Status = system(Command);
     if (Status == -1) {
@@ -7614,7 +7690,7 @@
 
 :- pragma foreign_proc("C",
     io.handle_system_command_exit_code(Status0::in) = (Status::out),
-    [will_not_call_mercury, thread_safe, promise_pure],
+    [will_not_call_mercury, thread_safe, promise_pure, does_not_affect_liveness],
 "
     #if defined (WIFEXITED) && defined (WEXITSTATUS) && \
             defined (WIFSIGNALED) && defined (WTERMSIG)
@@ -7816,7 +7892,7 @@
 
 :- pragma foreign_proc("C",
     io.getenv(Var::in, Value::out),
-    [will_not_call_mercury, tabled_for_io],
+    [will_not_call_mercury, tabled_for_io, does_not_affect_liveness],
 "{
     Value = getenv(Var);
     SUCCESS_INDICATOR = (Value != 0);
@@ -7864,7 +7940,7 @@
 
 :- pragma foreign_proc("C",
     io.putenv(VarAndValue::in),
-    [will_not_call_mercury, tabled_for_io],
+    [will_not_call_mercury, tabled_for_io, does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = (putenv(VarAndValue) == 0);
 ").
@@ -7968,7 +8044,8 @@
 :- pragma foreign_proc("C",
     io.do_make_temp(Dir::in, Prefix::in, Sep::in, FileName::out,
         Error::out, ErrorMessage::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     /*
     ** Constructs a temporary name by concatenating Dir, `/', the first 5 chars
@@ -8256,7 +8333,8 @@
 :- pragma foreign_proc("C",
     io.remove_file_2(FileName::in, RetVal::out, RetStr::out,
         IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     RetVal = remove(FileName);
     ML_maybe_make_err_msg(RetVal != 0, errno, ""remove failed: "",
@@ -8320,7 +8398,8 @@
 :- pragma foreign_proc("C",
     io.rename_file_2(OldFileName::in, NewFileName::in, RetVal::out,
         RetStr::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
     RetVal = rename(OldFileName, NewFileName);
     ML_maybe_make_err_msg(RetVal != 0, errno, ""rename failed: "",
@@ -8379,7 +8458,8 @@
 
 :- pragma foreign_proc("C",
     io.have_symlinks,
-    [will_not_call_mercury, promise_pure, thread_safe],
+    [will_not_call_mercury, promise_pure, thread_safe,
+        does_not_affect_liveness],
 "
 #if defined(MR_HAVE_SYMLINK) && defined(MR_HAVE_READLINK)
     SUCCESS_INDICATOR = MR_TRUE;
@@ -8408,7 +8488,8 @@
 :- pragma foreign_proc("C",
     io.make_symlink_2(FileName::in, LinkFileName::in, Status::out,
         IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
 #ifdef MR_HAVE_SYMLINK
     Status = (symlink(FileName, LinkFileName) == 0);
@@ -8438,7 +8519,8 @@
 :- pragma foreign_proc("C",
     io.read_symlink_2(FileName::in, TargetFileName::out,
         Status::out, Error::out, IO0::di, IO::uo),
-    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe],
+    [will_not_call_mercury, promise_pure, tabled_for_io, thread_safe,
+        does_not_affect_liveness],
 "{
 #ifdef MR_HAVE_READLINK
   #ifndef PATH_MAX
Index: library/lexer.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/lexer.m,v
retrieving revision 1.51
diff -u -b -r1.51 lexer.m
--- library/lexer.m	1 Dec 2006 15:04:37 -0000	1.51
+++ library/lexer.m	10 Jan 2007 04:25:43 -0000
@@ -1172,7 +1172,8 @@
 
 :- pragma foreign_proc("C",
     backend_unicode_encoding_int = (EncodingInt::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     EncodingInt = 0;
 ").
Index: library/math.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/math.m,v
retrieving revision 1.56
diff -u -b -r1.56 math.m
--- library/math.m	27 Dec 2006 03:18:04 -0000	1.56
+++ library/math.m	10 Jan 2007 04:27:50 -0000
@@ -262,7 +262,8 @@
 
 :- pragma foreign_proc("C",
     math_domain_checks,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifdef ML_OMIT_MATH_DOMAIN_CHECKS
     SUCCESS_INDICATOR = MR_FALSE;
@@ -295,7 +296,8 @@
     % Pythagoras' number
 :- pragma foreign_proc("C",
     math.pi = (Pi::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Pi = ML_FLOAT_PI;
 ").
@@ -321,7 +323,8 @@
     % Base of natural logarithms
 :- pragma foreign_proc("C",
     math.e = (E::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     E = ML_FLOAT_E;
 ").
@@ -346,7 +349,8 @@
 
 :- pragma foreign_proc("C",
     math.ceiling(Num::in) = (Ceil::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Ceil = ceil(Num);
 ").
@@ -365,7 +369,8 @@
 
 :- pragma foreign_proc("C",
     math.floor(Num::in) = (Floor::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Floor = floor(Num);
 ").
@@ -384,7 +389,8 @@
 
 :- pragma foreign_proc("C",
     math.round(Num::in) = (Rounded::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Rounded = floor(Num+0.5);
 ").
@@ -417,7 +423,8 @@
 
 :- pragma foreign_proc("C",
     math.sqrt_2(X::in) = (SquareRoot::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SquareRoot = sqrt(X);
 ").
@@ -488,7 +495,8 @@
 
 :- pragma foreign_proc("C",
     math.pow_2(X::in, Y::in) = (Res::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Res = pow(X, Y);
 ").
@@ -509,7 +517,8 @@
 
 :- pragma foreign_proc("C",
     math.exp(X::in) = (Exp::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Exp = exp(X);
 ").
@@ -537,7 +546,8 @@
 
 :- pragma foreign_proc("C",
     math.ln_2(X::in) = (Log::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Log = log(X);
 ").
@@ -565,7 +575,8 @@
 
 :- pragma foreign_proc("C",
     math.log10_2(X::in) = (Log10::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Log10 = log10(X);
 ").
@@ -589,7 +600,8 @@
 
 :- pragma foreign_proc("C",
     math.log2_2(X::in) = (Log2::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Log2 = log(X) / ML_FLOAT_LN2;
 ").
@@ -624,7 +636,8 @@
 
 :- pragma foreign_proc("C",
     math.log_2(B::in, X::in) = (Log::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Log = log(X) / log(B);
 ").
@@ -639,7 +652,8 @@
 
 :- pragma foreign_proc("C",
     math.sin(X::in) = (Sin::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Sin = sin(X);
 ").
@@ -658,7 +672,8 @@
 
 :- pragma foreign_proc("C",
     math.cos(X::in) = (Cos::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Cos = cos(X);
 ").
@@ -677,7 +692,8 @@
 
 :- pragma foreign_proc("C",
     math.tan(X::in) = (Tan::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Tan = tan(X);
 ").
@@ -710,7 +726,8 @@
 
 :- pragma foreign_proc("C",
     math.asin_2(X::in) = (ASin::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ASin = asin(X);
 ").
@@ -743,7 +760,8 @@
 
 :- pragma foreign_proc("C",
     math.acos_2(X::in) = (ACos::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ACos = acos(X);
 ").
@@ -763,7 +781,8 @@
 
 :- pragma foreign_proc("C",
     math.atan(X::in) = (ATan::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ATan = atan(X);
 ").
@@ -782,7 +801,8 @@
 
 :- pragma foreign_proc("C",
     math.atan2(Y::in, X::in) = (ATan2::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     ATan2 = atan2(Y, X);
 ").
@@ -801,7 +821,8 @@
 
 :- pragma foreign_proc("C",
     math.sinh(X::in) = (Sinh::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Sinh = sinh(X);
 ").
@@ -817,7 +838,8 @@
 
 :- pragma foreign_proc("C",
     math.cosh(X::in) = (Cosh::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Cosh = cosh(X);
 ").
@@ -833,7 +855,8 @@
 
 :- pragma foreign_proc("C",
     math.tanh(X::in) = (Tanh::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Tanh = tanh(X);
 ").
Index: library/private_builtin.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/private_builtin.m,v
retrieving revision 1.164
diff -u -b -r1.164 private_builtin.m
--- library/private_builtin.m	23 Oct 2006 11:38:42 -0000	1.164
+++ library/private_builtin.m	10 Jan 2007 04:14:21 -0000
@@ -173,7 +173,8 @@
 
 :- pragma foreign_proc("C",
     builtin_strcmp(Res::out, S1::in, S2::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Res = strcmp(S1, S2);
 ").
@@ -552,7 +553,8 @@
 :- pragma foreign_proc("C",
     type_info_from_typeclass_info(TypeClassInfo::in, Index::in,
         TypeInfo::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     TypeInfo = MR_typeclass_info_param_type_info(TypeClassInfo, Index);
 ").
@@ -560,7 +562,8 @@
 :- pragma foreign_proc("C",
     unconstrained_type_info_from_typeclass_info(TypeClassInfo::in,
         Index::in, TypeInfo::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     TypeInfo = MR_typeclass_info_instance_tvar_type_info(TypeClassInfo, Index);
 ").
@@ -568,7 +571,8 @@
 :- pragma foreign_proc("C",
     superclass_from_typeclass_info(TypeClassInfo0::in, Index::in,
         TypeClassInfo::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     TypeClassInfo =
         MR_typeclass_info_superclass_info(TypeClassInfo0, Index);
@@ -577,7 +581,8 @@
 :- pragma foreign_proc("C",
     instance_constraint_from_typeclass_info(TypeClassInfo0::in,
         Index::in, TypeClassInfo::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     TypeClassInfo =
         MR_typeclass_info_arg_typeclass_info(TypeClassInfo0, Index);
@@ -701,7 +706,7 @@
 
 :- pragma foreign_proc("C",
     reset_ticket_undo(Ticket::in),
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_reset_ticket(Ticket, MR_undo);
@@ -710,7 +715,7 @@
 
 :- pragma foreign_proc("C",
     reset_ticket_commit(Ticket::in),
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_reset_ticket(Ticket, MR_commit);
@@ -719,7 +724,7 @@
 
 :- pragma foreign_proc("C",
     reset_ticket_solve(Ticket::in),
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_reset_ticket(Ticket, MR_solve);
@@ -728,7 +733,7 @@
 
 :- pragma foreign_proc("C",
     discard_ticket,
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_discard_ticket();
@@ -737,7 +742,7 @@
 
 :- pragma foreign_proc("C",
     prune_ticket,
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_prune_ticket();
@@ -746,7 +751,7 @@
 
 :- pragma foreign_proc("C",
     mark_ticket_stack(TicketCounter::out),
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_mark_ticket_stack(TicketCounter);
@@ -757,7 +762,7 @@
 
 :- pragma foreign_proc("C",
     prune_tickets_to(TicketCounter::in),
-    [will_not_call_mercury, thread_safe],
+    [will_not_call_mercury, thread_safe, does_not_affect_liveness],
 "
 #ifdef MR_USE_TRAIL
     MR_prune_tickets_to(TicketCounter);
@@ -993,7 +998,7 @@
     [will_not_call_mercury, thread_safe, will_not_modify_trail],
 "
 #ifdef MR_NATIVE_GC
-    *(MR_Word *)Pointer =
+    * (MR_Word *) Pointer =
         MR_agc_deep_copy(* (MR_Word *) Pointer, (MR_TypeInfo) TypeInfo_for_T,
             MR_ENGINE(MR_eng_heap_zone2->MR_zone_min),
             MR_ENGINE(MR_eng_heap_zone2->MR_zone_hardmax));
@@ -1012,7 +1017,8 @@
 
 :- pragma foreign_proc("C",
     mark_hp(SavedHeapPointer::out),
-    [will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifndef MR_CONSERVATIVE_GC
     MR_mark_hp(SavedHeapPointer);
@@ -1024,7 +1030,8 @@
 
 :- pragma foreign_proc("C",
     restore_hp(SavedHeapPointer::in),
-    [will_not_call_mercury, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
 #ifndef MR_CONSERVATIVE_GC
     MR_restore_hp(SavedHeapPointer);
@@ -1550,7 +1557,8 @@
 
 :- pragma foreign_proc("C",
     trace_evaluate_runtime_condition,
-    [will_not_call_mercury, thread_safe, promise_semipure],
+    [will_not_call_mercury, thread_safe, promise_semipure,
+        does_not_affect_liveness],
 "
     /* All uses of this predicate should override the body. */
     MR_fatal_error(""trace_evaluate_runtime_condition called"");
Index: library/string.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/string.m,v
retrieving revision 1.252
diff -u -b -r1.252 string.m
--- library/string.m	7 Dec 2006 05:10:30 -0000	1.252
+++ library/string.m	10 Jan 2007 04:14:21 -0000
@@ -976,6 +976,9 @@
     PreLen = length(Prefix),
     PreLen =< Len,
     prefix_2_iii(String, Prefix, PreLen - 1).
+string.prefix(String::in, Prefix::out) :-
+    Len = length(String),
+    prefix_2_ioii(String, Prefix, 0, Len).
 
 :- pred prefix_2_iii(string::in, string::in, int::in) is semidet.
 
@@ -987,10 +990,6 @@
         true
     ).
 
-string.prefix(String::in, Prefix::out) :-
-    Len = length(String),
-    prefix_2_ioii(String, Prefix, 0, Len).
-
 :- pred prefix_2_ioii(string::in, string::out, int::in, int::in) is multi.
 
 prefix_2_ioii(String, Prefix, PreLen, _Len) :-
@@ -1007,6 +1006,9 @@
     PreLen = length(Suffix),
     PreLen =< Len,
     suffix_2_iiii(String, Suffix, 0, Len - PreLen, PreLen).
+string.suffix(String::in, Suffix::out) :-
+    Len = length(String),
+    suffix_2_ioii(String, Suffix, 0, Len).
 
 :- pred suffix_2_iiii(string::in, string::in, int::in, int::in, int::in)
     is semidet.
@@ -1020,10 +1022,6 @@
         true
     ).
 
-string.suffix(String::in, Suffix::out) :-
-    Len = length(String),
-    suffix_2_ioii(String, Suffix, 0, Len).
-
 :- pred suffix_2_ioii(string::in, string::out, int::in, int::in) is multi.
 
 suffix_2_ioii(String, Suffix, SufLen, Len) :-
@@ -1169,7 +1167,8 @@
 
 :- pragma foreign_proc("C",
     string.to_char_list(Str::in, CharList::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_ConstString p = Str + strlen(Str);
     CharList = MR_list_empty_msg(MR_PROC_LABEL);
@@ -1182,7 +1181,8 @@
 
 :- pragma foreign_proc("C",
     string.to_char_list(Str::uo, CharList::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     /* mode (uo, in) is det */
     MR_Word char_list_ptr;
@@ -1250,7 +1250,8 @@
 
 :- pragma foreign_proc("C",
     string.from_rev_char_list(Chars::in, Str::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Word list_ptr;
     MR_Word size, len;
@@ -1391,7 +1392,8 @@
     % the amount of garbage created.
 :- pragma foreign_proc("C",
     string.append_list(Strs::in) = (Str::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Word list = Strs;
     MR_Word tmp;
@@ -1423,7 +1425,8 @@
     % garbage created.
 :- pragma foreign_proc("C",
     string.join_list(Sep::in, Strs::in) = (Str::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Word list;
     MR_Word tmp;
@@ -1521,7 +1524,8 @@
 :- pragma foreign_proc("C",
     sub_string_search_start(WholeString::in, Pattern::in, BeginAt::in,
         Index::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     char *match;
     match = strstr(WholeString + BeginAt, Pattern);
@@ -2008,7 +2012,8 @@
 :- pred using_sprintf is semidet.
 
 :- pragma foreign_proc("C", using_sprintf,
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = MR_TRUE;
 ").
@@ -2085,7 +2090,8 @@
 
 :- pragma foreign_proc("C",
     int_length_modifer = (LengthModifier::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_make_aligned_string(LengthModifier, MR_INTEGER_LENGTH_MODIFIER);
 }").
@@ -2103,7 +2109,8 @@
 
 :- pragma foreign_proc("C",
     native_format_float(FormatStr::in, Val::in) = (Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_save_transient_hp();
     Str = MR_make_string(MR_PROC_LABEL, FormatStr, (double) Val);
@@ -2122,7 +2129,8 @@
 
 :- pragma foreign_proc("C",
     native_format_int(FormatStr::in, Val::in) = (Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_save_transient_hp();
     Str = MR_make_string(MR_PROC_LABEL, FormatStr, Val);
@@ -2141,7 +2149,8 @@
 
 :- pragma foreign_proc("C",
     native_format_string(FormatStr::in, Val::in) = (Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_save_transient_hp();
     Str = MR_make_string(MR_PROC_LABEL, FormatStr, Val);
@@ -2160,7 +2169,8 @@
 
 :- pragma foreign_proc("C",
     native_format_char(FormatStr::in, Val::in) = (Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_save_transient_hp();
     Str = MR_make_string(MR_PROC_LABEL, FormatStr, Val);
@@ -3001,7 +3011,8 @@
 
 :- pragma foreign_proc("C",
     string.float_to_string(Flt::in, Str::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     /*
     ** For efficiency reasons we duplicate the C implementation
@@ -3060,7 +3071,8 @@
 
 :- pragma foreign_proc("C",
     string.lowlevel_float_to_string(Flt::in, Str::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     /*
     ** Note any changes here will require the same changes in
@@ -3099,7 +3111,8 @@
 
 :- pragma foreign_proc("C",
     string.to_float(FloatString::in, FloatVal::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     /*
     ** The %c checks for any erroneous characters appearing after the float;
@@ -3189,7 +3202,8 @@
     % shouldn't be considered to be part of the string itself.
 :- pragma foreign_proc("C",
     string.contains_char(Str::in, Ch::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = (strchr(Str, Ch) != NULL) && Ch != '\\0';
 ").
@@ -3237,7 +3251,8 @@
 % We should consider making this routine a compiler built-in.
 :- pragma foreign_proc("C",
     string.index_check(Index::in, Length::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     /*
     ** We do not test for negative values of Index because (a) MR_Unsigned
@@ -3262,7 +3277,8 @@
 
 :- pragma foreign_proc("C",
     string.unsafe_index(Str::in, Index::in, Ch::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Ch = Str[Index];
 ").
@@ -3323,7 +3339,8 @@
 
 :- pragma foreign_proc("C",
     string.set_char(Ch::in, Index::in, Str0::in, Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     size_t len = strlen(Str0);
     if ((MR_Unsigned) Index >= len) {
@@ -3355,7 +3372,7 @@
 
 % :- pragma foreign_proc("C",
 %   string.set_char(Ch::in, Index::in, Str0::di, Str::uo),
-%   [will_not_call_mercury, promise_pure, thread_safe],
+%   [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 % "
 %   if ((MR_Unsigned) Index >= strlen(Str0)) {
 %       SUCCESS_INDICATOR = MR_FALSE;
@@ -3384,7 +3401,8 @@
 
 :- pragma foreign_proc("C",
     string.unsafe_set_char(Ch::in, Index::in, Str0::in, Str::out),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     size_t len = strlen(Str0);
     MR_allocate_aligned_string_msg(Str, len, MR_PROC_LABEL);
@@ -3408,7 +3426,7 @@
 
 % :- pragma foreign_proc("C",
 %   string.unsafe_set_char(Ch::in, Index::in, Str0::di, Str::uo),
-%   [will_not_call_mercury, promise_pure, thread_safe],
+%   [will_not_call_mercury, promise_pure, thread_safe, does_not_affect_liveness],
 % "
 %   Str = Str0;
 %   MR_set_char(Str, Index, Ch);
@@ -3430,9 +3448,12 @@
 
 /*-----------------------------------------------------------------------*/
 
+:- pragma promise_equivalent_clauses(string.length/2).
+
 :- pragma foreign_proc("C",
     string.length(Str::in, Length::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Length = strlen(Str);
 ").
@@ -3451,7 +3472,8 @@
 
 :- pragma foreign_proc("C",
     string.length(Str::ui, Length::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     Length = strlen(Str);
 ").
@@ -3467,8 +3489,6 @@
     Length = Str.length();
 ").
 
-:- pragma promise_equivalent_clauses(string.length/2).
-
 string.length(Str0, Len) :-
     % XXX This copy is only necessary because of the ui.
     copy(Str0, Str),
@@ -3500,7 +3520,8 @@
 
 :- pragma foreign_proc("C",
     string.append_iii(S1::in, S2::in, S3::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     size_t len_1 = strlen(S1);
     SUCCESS_INDICATOR = (
@@ -3523,7 +3544,8 @@
 
 :- pragma foreign_proc("C",
     string.append_ioi(S1::in, S2::uo, S3::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     size_t len_1, len_2, len_3;
 
@@ -3561,7 +3583,8 @@
 
 :- pragma foreign_proc("C",
     string.append_iio(S1::in, S2::in, S3::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     size_t len_1, len_2;
     len_1 = strlen(S1);
@@ -3606,7 +3629,8 @@
 
 :- pragma foreign_proc("C",
     string.append_ooi_3(S1Len::in, S3Len::in, S1::out, S2::out, S3::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_allocate_aligned_string_msg(S1, S1Len, MR_PROC_LABEL);
     MR_memcpy(S1, S3, S1Len);
@@ -3659,7 +3683,8 @@
 
 :- pragma foreign_proc("C",
     string.substring(Str::in, Start::in, Count::in, SubString::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Integer  len;
     MR_Word     tmp;
@@ -3679,7 +3704,8 @@
 
 :- pragma foreign_proc("C",
     string.unsafe_substring(Str::in, Start::in, Count::in, SubString::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Integer len;
 
@@ -3702,7 +3728,8 @@
 
 :- pragma foreign_proc("C",
     string.split(Str::in, Count::in, Left::uo, Right::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     MR_Integer  len;
     MR_Word     tmp;
@@ -3771,7 +3798,8 @@
 
 :- pragma foreign_proc("C",
     string.first_char(Str::in, First::in, Rest::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = (
         Str[0] == First &&
@@ -3801,7 +3829,8 @@
 
 :- pragma foreign_proc("C",
     string.first_char(Str::in, First::uo, Rest::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     First = Str[0];
     SUCCESS_INDICATOR = (First != '\\0' && strcmp(Str + 1, Rest) == 0);
@@ -3834,7 +3863,8 @@
 
 :- pragma foreign_proc("C",
     string.first_char(Str::in, First::in, Rest::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     if (Str[0] != First || First == '\\0') {
         SUCCESS_INDICATOR = MR_FALSE;
@@ -3879,7 +3909,8 @@
 
 :- pragma foreign_proc("C",
     string.first_char(Str::in, First::uo, Rest::uo),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     First = Str[0];
     if (First == '\\0') {
@@ -3924,7 +3955,8 @@
 
 :- pragma foreign_proc("C",
     string.first_char(Str::uo, First::in, Rest::in),
-    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, thread_safe, will_not_modify_trail,
+        does_not_affect_liveness],
 "{
     size_t len = strlen(Rest) + 1;
     MR_allocate_aligned_string_msg(Str, len, MR_PROC_LABEL);
Index: library/table_builtin.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/table_builtin.m,v
retrieving revision 1.55
diff -u -b -r1.55 table_builtin.m
--- library/table_builtin.m	31 Aug 2006 11:09:53 -0000	1.55
+++ library/table_builtin.m	10 Jan 2007 04:44:42 -0000
@@ -241,7 +241,7 @@
         AnswerTableLookups::out, AnswerTableNotDupl::out,
         PrevAnswerTableLookups::out, PrevAnswerTableNotDupl::out,
         _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
     AnswerTable = ( Info->MR_pt_has_answer_table ? 1 : 0 );
     Inputs = Info->MR_pt_num_inputs;
@@ -333,7 +333,7 @@
         PrevNumInsertProbes::out, PrevNumLookupProbes::out,
         PrevNumResizes::out, PrevNumResizesOld::out, PrevNumResizesNew::out,
         _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
     MR_TableStepStats   *cur;
     MR_TableStepStats   *prev;
@@ -379,7 +379,7 @@
         PrevNumInsertProbes::out, PrevNumLookupProbes::out,
         PrevNumResizes::out, PrevNumResizesOld::out, PrevNumResizesNew::out,
         _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
     MR_TableStepStats   *cur;
     MR_TableStepStats   *prev;
@@ -413,7 +413,7 @@
 
 :- pragma foreign_proc("C",
     copy_current_stats_to_prev(Info::in, _IO0::di, _IO::uo),
-    [will_not_call_mercury, promise_pure],
+    [will_not_call_mercury, promise_pure, does_not_affect_liveness],
 "
     int i;
     MR_TableStepStats   *cur;
@@ -483,35 +483,35 @@
 
 :- pragma foreign_proc("C",
     table_loop_setup(T::in, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_loop_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_loop_setup_shortcut(T0::in, T::out, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_loop_setup_shortcut(T0, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_loop_mark_as_inactive(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_loop_mark_as_inactive(MR_TABLE_DEBUG_BOOL, T);
 ").
 
 :- pragma foreign_proc("C",
     table_loop_mark_as_inactive_and_fail(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_loop_mark_as_inactive_and_fail(MR_TABLE_DEBUG_BOOL, T);
 ").
 
 :- pragma foreign_proc("C",
     table_loop_mark_as_active_and_fail(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_loop_mark_as_active_and_fail(MR_TABLE_DEBUG_BOOL, T);
 ").
@@ -676,77 +676,77 @@
 
 :- pragma foreign_proc("C",
     table_memo_det_setup(T::in, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_det_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_det_setup_shortcut(T0::in, T::out, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_det_setup_shortcut(T0, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_semi_setup(T::in, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_semi_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_semi_setup_shortcut(T0::in, T::out, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_semi_setup_shortcut(T0, T, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_non_setup(T0::in, Record::out, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_non_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, Record, Status);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_mark_as_failed(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_mark_as_failed(MR_TABLE_DEBUG_BOOL, T);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_mark_as_succeeded(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_mark_as_succeeded(MR_TABLE_DEBUG_BOOL, T);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_mark_as_incomplete(R::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_mark_as_incomplete(MR_TABLE_DEBUG_BOOL, R);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_mark_as_active_and_fail(R::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_mark_as_active_and_fail(MR_TABLE_DEBUG_BOOL, R);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_mark_as_complete_and_fail(R::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_mark_as_complete_and_fail(MR_TABLE_DEBUG_BOOL, R);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_create_answer_block(T::in, Size::in, AnswerBlock::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_create_answer_block(MR_TABLE_DEBUG_BOOL,
         T, Size, AnswerBlock);
@@ -754,35 +754,35 @@
 
 :- pragma foreign_proc("C",
     table_memo_fill_answer_block_shortcut(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_fill_answer_block_shortcut(T);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_get_answer_block(T::in, AnswerBlock::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_memo_get_answer_block(MR_TABLE_DEBUG_BOOL, T, AnswerBlock);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_get_answer_block_shortcut(T::in),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_memo_get_answer_block_shortcut(T);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_non_get_answer_table(R::in, AT::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_memo_non_get_answer_table(MR_TABLE_DEBUG_BOOL, R, AT);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_non_answer_is_not_duplicate(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_non_answer_is_not_duplicate(MR_TABLE_DEBUG_BOOL,
         T, SUCCESS_INDICATOR);
@@ -790,7 +790,7 @@
 
 :- pragma foreign_proc("C",
     table_memo_non_answer_is_not_duplicate_shortcut(R::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_non_answer_is_not_duplicate_shortcut(R,
         SUCCESS_INDICATOR);
@@ -798,14 +798,14 @@
 
 :- pragma foreign_proc("C",
     table_memo_non_create_answer_block_shortcut(R::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_memo_non_create_answer_block_shortcut(R::in);
 ").
 
 :- pragma foreign_proc("C",
     table_memo_non_return_all_shortcut(R::in),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_memo_non_return_all_shortcut(R);
 ").
@@ -1032,7 +1032,7 @@
 
 :- pragma foreign_proc("C",
     table_io_in_range(T::out, Counter::out, Start::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_io_in_range(MR_TABLE_DEBUG_BOOL, T, Counter, Start,
         SUCCESS_INDICATOR);
@@ -1040,7 +1040,7 @@
 
 :- pragma foreign_proc("C",
     table_io_has_occurred(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_io_has_occurred(MR_TABLE_DEBUG_BOOL, T, SUCCESS_INDICATOR);
 ").
@@ -1049,14 +1049,14 @@
 
 :- pragma foreign_proc("C",
     table_io_left_bracket_unitized_goal(TraceEnabled::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_io_left_bracket_unitized_goal(TraceEnabled);
 ").
 
 :- pragma foreign_proc("C",
     table_io_right_bracket_unitized_goal(TraceEnabled::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_io_right_bracket_unitized_goal(TraceEnabled);
 ").
@@ -1182,7 +1182,7 @@
 
 :- pragma foreign_proc("C",
     table_mm_setup(T::in, Subgoal::out, Status::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_mm_setup(MR_TABLE_DEBUG_BOOL, MR_FALSE, T, Subgoal, Status);
 ").
@@ -1197,21 +1197,21 @@
 
 :- pragma foreign_proc("C",
     table_mm_return_all_shortcut(AnswerBlock::in),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_mm_return_all_shortcut(AnswerBlock);
 ").
 
 :- pragma foreign_proc("C",
     table_mm_get_answer_table(Subgoal::in, AnswerTable::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_mm_get_answer_table(MR_TABLE_DEBUG_BOOL, Subgoal, AnswerTable);
 ").
 
 :- pragma foreign_proc("C",
     table_mm_answer_is_not_duplicate(TrieNode::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_mm_answer_is_not_duplicate(MR_TABLE_DEBUG_BOOL, TrieNode,
         SUCCESS_INDICATOR);
@@ -1219,7 +1219,7 @@
 
 :- pragma foreign_proc("C",
     table_mm_answer_is_not_duplicate_shortcut(Subgoal::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1232,7 +1232,7 @@
 
 :- pragma foreign_proc("C",
     table_mm_create_answer_block(Subgoal::in, Size::in, AnswerBlock::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_mm_create_answer_block(MR_TABLE_DEBUG_BOOL,
         Subgoal, Size, AnswerBlock);
@@ -1240,7 +1240,7 @@
 
 :- pragma foreign_proc("C",
     table_mm_fill_answer_block_shortcut(Subgoal::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_mm_fill_answer_block_shortcut(Subgoal);
 ").
@@ -1351,12 +1351,6 @@
 :- impure pred table_mmos_create_answer_block(ml_generator::in,
     int::in, ml_answer_block::out) is det.
 
-    % Return the base of the answer table trie for the subgoal of the
-    % given generator.
-    %
-:- semipure pred table_mmos_get_answer_table(ml_generator::in,
-    ml_trie_node::out) is det.
-
     % Return the given answer to the consumer(s) that requested it.
     %
 :- impure pred table_mmos_return_answer(ml_generator::in, ml_answer_block::in)
@@ -1389,7 +1383,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_save_inputs,
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1401,7 +1395,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_setup_consumer(T::in, GeneratorPred::in, Consumer::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1414,7 +1408,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_answer_is_not_duplicate(T::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1427,7 +1421,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_answer_is_not_duplicate_shortcut(G::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1443,7 +1437,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_restore_answers(AnswerBlock::in),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1455,17 +1449,8 @@
 ").
 
 :- pragma foreign_proc("C",
-    table_mmos_get_answer_table(Generator::in, TrieNode::out),
-    [will_not_call_mercury, promise_semipure],
-"
-    /*
-    MR_tbl_mmos_get_answer_table(Generator, TrieNode);
-    */
-").
-
-:- pragma foreign_proc("C",
     table_mmos_pickup_inputs(Generator::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     ** The body of this predicate doesn't matter, because it will never be
@@ -1479,7 +1464,7 @@
 :- pragma foreign_proc("C",
     table_mmos_create_answer_block(Generator::in, BlockSize::in,
         AnswerBlock::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     MR_tbl_mmos_create_answer_block(Generator, BlockSize, AnswerBlock);
@@ -1488,7 +1473,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_return_answer(Generator::in, AnswerBlock::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     MR_tbl_mmos_return_answer(Generator, AnswerBlock);
@@ -1497,7 +1482,7 @@
 
 :- pragma foreign_proc("C",
     table_mmos_completion(Generator::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     /*
     MR_tbl_mmos_completion(Generator);
@@ -1512,11 +1497,6 @@
     % Required only to avoid warnings; never executed.
     pretend_to_generate_value(Consumer).
 
-table_mmos_get_answer_table(_, TrieNode) :-
-    semipure private_builtin.semip,
-    % Required only to avoid warnings; never executed.
-    pretend_to_generate_value(TrieNode).
-
 table_mmos_create_answer_block(_, _, AnswerBlock) :-
     impure private_builtin.imp,
     % Required only to avoid warnings; never executed.
@@ -1714,14 +1694,14 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_int(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_int(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
 ").
 
 :- pragma foreign_proc("C",
     table_lookup_insert_start_int(T0::in, S::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_start_int(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE,
         T0, S, V, T);
@@ -1729,28 +1709,28 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_char(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_char(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
 ").
 
 :- pragma foreign_proc("C",
     table_lookup_insert_string(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_string(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
 ").
 
 :- pragma foreign_proc("C",
     table_lookup_insert_float(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_float(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
 ").
 
 :- pragma foreign_proc("C",
     table_lookup_insert_enum(T0::in, R::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_enum(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
         R, V, T);
@@ -1758,7 +1738,7 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_user(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_user(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
         TypeInfo_for_T, V, T);
@@ -1766,7 +1746,7 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_user_fast_loose(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_user_addr(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
         TypeInfo_for_T, V, T);
@@ -1774,7 +1754,7 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_poly(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_poly(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
         TypeInfo_for_T, V, T);
@@ -1782,7 +1762,7 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_poly_fast_loose(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_poly_addr(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0,
         TypeInfo_for_T, V, T);
@@ -1790,14 +1770,14 @@
 
 :- pragma foreign_proc("C",
     table_lookup_insert_typeinfo(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_typeinfo(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE, T0, V, T);
 ").
 
 :- pragma foreign_proc("C",
     table_lookup_insert_typeclassinfo(T0::in, V::in, T::out),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_lookup_insert_typeclassinfo(NULL, MR_TABLE_DEBUG_BOOL, MR_FALSE,
         T0, V, T);
@@ -1807,42 +1787,42 @@
 
 :- pragma foreign_proc("C",
     table_save_int_answer(AB::in, Offset::in, V::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_int_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_save_char_answer(AB::in, Offset::in, V::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_char_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_save_string_answer(AB::in, Offset::in, V::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_string_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_save_float_answer(AB::in, Offset::in, V::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_float_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_save_io_state_answer(AB::in, Offset::in, V::ui),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_io_state_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_save_any_answer(AB::in, Offset::in, V::in),
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_tbl_save_any_answer(MR_TABLE_DEBUG_BOOL, AB, Offset,
         TypeInfo_for_T, V);
@@ -1850,42 +1830,42 @@
 
 :- pragma foreign_proc("C",
     table_restore_int_answer(AB::in, Offset::in, V::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_int_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_restore_char_answer(AB::in, Offset::in, V::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_char_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_restore_string_answer(AB::in, Offset::in, V::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_string_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_restore_float_answer(AB::in, Offset::in, V::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_float_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_restore_io_state_answer(AB::in, Offset::in, V::uo),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_io_state_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
 
 :- pragma foreign_proc("C",
     table_restore_any_answer(AB::in, Offset::in, V::out),
-    [will_not_call_mercury, promise_semipure],
+    [will_not_call_mercury, promise_semipure, does_not_affect_liveness],
 "
     MR_tbl_restore_any_answer(MR_TABLE_DEBUG_BOOL, AB, Offset, V);
 ").
@@ -1895,7 +1875,7 @@
 
 :- pragma foreign_proc("C",
     table_report_statistics,
-    [will_not_call_mercury],
+    [will_not_call_mercury, does_not_affect_liveness],
 "
     MR_table_report_statistics(stderr);
 ").
Index: library/version_array.m
===================================================================
RCS file: /home/mercury/mercury1/repository/mercury/library/version_array.m,v
retrieving revision 1.12
diff -u -b -r1.12 version_array.m
--- library/version_array.m	27 Sep 2006 06:16:44 -0000	1.12
+++ library/version_array.m	10 Jan 2007 04:30:13 -0000
@@ -282,7 +282,8 @@
 
 :- pragma foreign_proc("C",
     version_array.empty = (VA::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     VA = MR_GC_NEW(struct ML_va);
 
@@ -294,7 +295,8 @@
 
 :- pragma foreign_proc("C",
     version_array.new(N::in, X::in) = (VA::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     MR_Integer  i;
     
@@ -311,7 +313,8 @@
 
 :- pragma foreign_proc("C",
     resize(VA0::in, N::in, X::in) = (VA::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     MR_Integer  i;
     MR_Integer  size_VA0;
@@ -337,8 +340,10 @@
 
 resize(N, X, VA, resize(VA, N, X)).
 
-:- pragma foreign_proc("C", size(VA::in) = (N::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+:- pragma foreign_proc("C",
+    size(VA::in) = (N::out),
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     N = ML_va_size(VA);
 ").
@@ -347,7 +352,8 @@
 
 :- pragma foreign_proc("C",
     get_if_in_range(VA::in, I::in, X::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = ML_va_get(VA, I, &X);
 ").
@@ -357,14 +363,16 @@
 
 :- pragma foreign_proc("C",
     set_if_in_range(VA0::in, I::in, X::in, VA::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     SUCCESS_INDICATOR = ML_va_set(VA0, I, X, &VA);
 ").
 
 :- pragma foreign_proc("C",
     unsafe_rewind(VA0::in) = (VA::out),
-    [will_not_call_mercury, promise_pure, will_not_modify_trail],
+    [will_not_call_mercury, promise_pure, will_not_modify_trail,
+        does_not_affect_liveness],
 "
     VA = ML_va_rewind(VA0);
 ").
cvs diff: Diffing mdbcomp
cvs diff: Diffing profiler
cvs diff: Diffing robdd
cvs diff: Diffing runtime
cvs diff: Diffing runtime/GETOPT
cvs diff: Diffing runtime/machdeps
cvs diff: Diffing samples
cvs diff: Diffing samples/c_interface
cvs diff: Diffing samples/c_interface/c_calls_mercury
cvs diff: Diffing samples/c_interface/cplusplus_calls_mercury
cvs diff: Diffing samples/c_interface/mercury_calls_c
cvs diff: Diffing samples/c_interface/mercury_calls_cplusplus
cvs diff: Diffing samples/c_interface/mercury_calls_fortran
cvs diff: Diffing samples/c_interface/simpler_c_calls_mercury
cvs diff: Diffing samples/c_interface/simpler_cplusplus_calls_mercury
cvs diff: Diffing samples/diff
cvs diff: Diffing samples/muz
cvs diff: Diffing samples/rot13
cvs diff: Diffing samples/solutions
cvs diff: Diffing samples/tests
cvs diff: Diffing samples/tests/c_interface
cvs diff: Diffing samples/tests/c_interface/c_calls_mercury
cvs diff: Diffing samples/tests/c_interface/cplusplus_calls_mercury
cvs diff: Diffing samples/tests/c_interface/mercury_calls_c
cvs diff: Diffing samples/tests/c_interface/mercury_calls_cplusplus
cvs diff: Diffing samples/tests/c_interface/mercury_calls_fortran
cvs diff: Diffing samples/tests/c_interface/simpler_c_calls_mercury
cvs diff: Diffing samples/tests/c_interface/simpler_cplusplus_calls_mercury
cvs diff: Diffing samples/tests/diff
cvs diff: Diffing samples/tests/muz
cvs diff: Diffing samples/tests/rot13
cvs diff: Diffing samples/tests/solutions
cvs diff: Diffing samples/tests/toplevel
cvs diff: Diffing scripts
cvs diff: Diffing slice
cvs diff: Diffing tests
cvs diff: Diffing tests/benchmarks
cvs diff: Diffing tests/debugger
cvs diff: Diffing tests/debugger/declarative
cvs diff: Diffing tests/dppd
cvs diff: Diffing tests/general
cvs diff: Diffing tests/general/accumulator
cvs diff: Diffing tests/general/string_format
cvs diff: Diffing tests/general/structure_reuse
cvs diff: Diffing tests/grade_subdirs
cvs diff: Diffing tests/hard_coded
cvs diff: Diffing tests/hard_coded/exceptions
cvs diff: Diffing tests/hard_coded/purity
cvs diff: Diffing tests/hard_coded/sub-modules
cvs diff: Diffing tests/hard_coded/typeclasses
cvs diff: Diffing tests/invalid
cvs diff: Diffing tests/invalid/purity
cvs diff: Diffing tests/misc_tests
cvs diff: Diffing tests/mmc_make
cvs diff: Diffing tests/mmc_make/lib
cvs diff: Diffing tests/par_conj
cvs diff: Diffing tests/recompilation
cvs diff: Diffing tests/tabling
Index: tests/tabling/oota.m
===================================================================
RCS file: /home/mercury/mercury1/repository/tests/tabling/oota.m,v
retrieving revision 1.2
diff -u -b -r1.2 oota.m
--- tests/tabling/oota.m	31 May 2004 04:13:27 -0000	1.2
+++ tests/tabling/oota.m	10 Jan 2007 09:10:56 -0000
@@ -1,3 +1,5 @@
+% vim: ts=4 sw=4 et ft=mercury
+%
 % Test taken from the XSB testsuite.
 %
 % Under local evaluation it results in:
@@ -10,7 +12,6 @@
 %
 % If d/4 is declared nondet, the program compiles without warnings,
 % but produces no answer when it executes.
-%
 
 :- module oota.
 
@@ -18,19 +19,49 @@
 
 :- import_module io.
 
-:- pred main(io::di, io::uo) is det.
+:- pred main(io::di, io::uo) is cc_multi.
 
 :- implementation.
 
-:- import_module std_util, int, list.
+:- import_module benchmarking.
+:- import_module int.
+:- import_module list.
+:- import_module maybe.
+:- import_module std_util.
+:- import_module string.
+:- import_module unit.
 
 main(!IO) :-
-	( d(Out) ->
-		io__write_string("D = ", !IO),
-		io__write_int(Out, !IO),
-		io__write_string("\n", !IO)
+    io.command_line_arguments(Args, !IO),
+    (
+        Args = [Arg],
+        string.to_int(Arg, NumRepeatsPrime)
+    ->
+        NumRepeats = NumRepeatsPrime
+    ;
+        NumRepeats = 1
+    ),
+    benchmark_det(benchmark_d, unit, MaybeD, NumRepeats, Time),
+    trace [compiletime(flag("benchmark_oota")), io(!S)] (
+        io.format("time taken: %d ms\n", [i(Time)], !S)
+    ),
+    (
+        MaybeD = yes(D),
+        io.write_string("D = ", !IO),
+        io.write_int(D, !IO),
+        io.write_string("\n", !IO)
+    ;
+        MaybeD = no,
+        io.write_string("failed\n", !IO)
+    ).
+
+:- pred benchmark_d(unit::in, maybe(int)::out) is det.
+
+benchmark_d(unit, MaybeD) :-
+    ( d(D) ->
+        MaybeD = yes(D)
 	;
-		io__write_string("failed\n", !IO)
+        MaybeD = no
 	).
 
 :- pred d(int::out) is semidet.
@@ -66,7 +97,7 @@
 :- pred min(list(int)::in, int::out) is semidet.
 
 min(S, M) :-
-	list__sort(S, [M|_]).
+    list.sort(S, [M|_]).
 
 :- pred ms(int::in, int::in, int::out) is semidet.
 
cvs diff: Diffing tests/term
cvs diff: Diffing tests/trailing
cvs diff: Diffing tests/valid
cvs diff: Diffing tests/warnings
cvs diff: Diffing tools
cvs diff: Diffing trace
cvs diff: Diffing util
cvs diff: Diffing vim
cvs diff: Diffing vim/after
cvs diff: Diffing vim/ftplugin
cvs diff: Diffing vim/syntax
--------------------------------------------------------------------------
mercury-reviews mailing list
Post messages to:       mercury-reviews at csse.unimelb.edu.au
Administrative Queries: owner-mercury-reviews at csse.unimelb.edu.au
Subscriptions:          mercury-reviews-request at csse.unimelb.edu.au
--------------------------------------------------------------------------



More information about the reviews mailing list