I have applied the input_mode_spec pragma to the two modules in the compiler that inspired it, ml_elim_nested.m and quantification.m. This raised two main issues. The first concerns how that pragma should apply to module interfaces, while the second is specific to the code now in quantification.m. ---------------------------------------------------------- Issue 1 The old interface of ml_elim_nested.m has no need for an input_mode_spec pragma, since it effectively has its effect manually applied already, declaring its only exported predicate like this: :- pred ml_elim_nested(mlds_target_lang, action, mlds, mlds). :- mode ml_elim_nested(in, in(hoist), in, out) is det. :- mode ml_elim_nested(in, in(chain), in, out) is det. For quantification.m, the picture is different. It exports several predicates that each take a nonlocals_to_recompute argument. This is an enum type with three values: :- type nonlocals_to_recompute ---> ord_nl_maybe_lambda % ordinary nonlocals computation ; ord_nl_no_lambda % ordinary nonlocals computation ; cg_nl_no_lambda. % code gen nonlocals computation The nonexported predicates of the module are currently pretty much all manually input mode specialized, which the new pragma would make make unnecessary. However, the current rule that you cannot use replace_in_mode in an input_mode_spec pragma in the interface has an unwelcome effect. This is that all exported predicates, such as implicitly_quantify_goal_general, - must have a procedure in which the nonlocals_to_recompute argument's mode is just "in", which means that - its calls to the local predicates of the module, such as implicitly_quantify_goal_2, can only call those of their procedures that also specify just "in" for their nonlocals_to_recompute arguments. This defeats the whole purpose of the input_mode_spec pragma. This is because many of the input-specialized procedures created by the pragma will be unreachable due to their callers not knowing the value of the input-specialized argument. The only ways I see around this problem are these. Way 1a: Have predicates such implicitly_quantify_goal_general replace each call to e.g. implicitly_quantify_goal_2 with a switch on NonLocalsToRecompute, in which the code of each arm contains the same call to implicitly_quantify_goal_2, but from a program point at which the value of NonLocalsToRecompute has been tested by the switch. This is, to put it politely, inelegant. (This lack of elegance could possibly be cured by a new scope around the call that basically asks the compiler to create that switch automatically, but I think that would be a massive overkill.) Way 1b: Allow input_mode_spec pragmas to specify replace_in_mode even in the interface. I don't like this, because it makes the actual mode declarations of the input specialized exported predicates into lies. Way 1c: Ask programmers to manually apply input specialization to exported predicate declarations, as the old ml_elim_nested.m has done for a *long* time. However, if we take this path, then there is no point in actually allowing input_mode_spec pragmas in module interfaces, and in fact it would be better if that asking was done, beside the manual, by the error message we generate for any input_mode_spec pragma in the interface. (This would also probably mean that input_mode_spec pragmas should be reclassified as impl pragmas, instead of the current decl pragma, but this is easily done.) Of those three alternatives, I would prefer Way 1c. It does require more work from the programmer writing the module, but it makes the module interface easier to understand for every programmer *reading* the module. ---------------------------------------------------------- Issue 2 As shown above, nonlocals_to_recompute has three values: :- type nonlocals_to_recompute ---> ord_nl_maybe_lambda % ordinary nonlocals computation ; ord_nl_no_lambda % ordinary nonlocals computation ; cg_nl_no_lambda. % code gen nonlocals computation When I applied way 1c above to quantification.m, I found that while there are plenty of callers of quantification.m's exported predicates that specify the first two values of nonlocals_to_recompute, there are absolutely NONE that specify the third. I tried to find out when the last call to quantification.m that passed cg_nl_no_lambda (or code_gen_nonlocals, the original name of that function symbol) was deleted, but I found that there NEVER WAS such a call. Simon added code_gen_nonlocals to quantification.m on 10 Feb 2000, in a commit that also added a fake code_gen_nonlocals field to hlds_goal_infos. It is fake because its getter and setter access the ordinary nonlocals instead: % The code-gen non-locals are always the same as the % non-locals when structure reuse is not being performed. goal_info_get_code_gen_nonlocals(GoalInfo) = goal_info_get_nonlocals(GoalInfo). % The code-gen non-locals are always the same as the % non-locals when structure reuse is not being performed. goal_info_set_code_gen_nonlocals(NonLocals, !GoalInfo) :- goal_info_set_nonlocals(NonLocals, !GoalInfo). I have no idea whether Simon intended to work on adding calls to actually compute the codegen nonlocals, or whether he intended to hand over that work to someone else, or what. (Simon's commits later that Feb all involved Aditi.) The emails about that commit are not in the m-rev archive, since that started in Oct 2000. I also don't know whether the people who later worked on structure sharing/reuse knew about the codegen nonlocals being fake. Peter, did you? The difference between ordinary and codegen nonlocals is definitely relevant to any work on structure reuse; no such system will work without it. This means that Simon's work on computing codegen nonlocals is potentially useful, though its usefulness is somewhat limited by the fact that this code couldn't have ever been tested, at least on the main trunk. (I don't know of any CVS branches for this work, but there may have been some.) The issue I am raising is this: this code may be useful in the future, but for now, it is a dead weight, complicating maintenance, and slowing compilation. I can see three ways forward. Way 2a: keep the code handling cg_nl_no_lambda as is, by adding a mode to at least one exported predicate that has in(cg_nl_no_lambda) as the mode of the nonlocals_to_recompute argument. Way 2b: keep the code handling cg_nl_no_lambda, but comment it out, and comment the cg_nl_no_lambda alternative out of the nonlocals_to_recompute type. Do NOT specify in(cg_nl_no_lambda) as the mode of the nonlocals_to_recompute argument for ANY exported predicate. Note that commenting out the parts of predicates that handle cg_nl_no_lambda in many cases leaves some of the inputs of the predicates involved unused. Way 2c: go beyond 2b by deleting those now-unused inputs, and where relevant, propagate these deletions upward. I would be ok with any of 2a/b/c, though I have a slight preference for 2c. What do you guys think?