[m-rev.] for review: exception analysis of generic calls
Julien Fischer
juliensf at cs.mu.OZ.AU
Mon Jul 25 18:16:22 AEST 2005
For review by anyone.
Estimated hours taken: 7
Branches: main
Add support for exception analysis through some generic_call sites.
At the moment this is restricted to higher-order calls, although
we may eventually extend it to method calls as well.
As with the recent change to generic_calls for termination analysis,
this is currently of limited usefulness because the procedures
dependency graph does not record information about higher-order calls.
I'll add some test cases when the dependency graph has been changed
to include this information.
compiler/exception_analysis.m:
Use the results of closure analysis to perform exception
analysis at generic_call sites.
Julien.
Index: compiler/exception_analysis.m
===================================================================
RCS file: /home/mercury1/repository/mercury/compiler/exception_analysis.m,v
retrieving revision 1.9
diff -u -r1.9 exception_analysis.m
--- compiler/exception_analysis.m 14 Apr 2005 07:47:31 -0000 1.9
+++ compiler/exception_analysis.m 25 Jul 2005 07:59:58 -0000
@@ -267,20 +267,23 @@
( goal_info_get_determinism(GoalInfo, erroneous) ->
!:Result = !.Result ^ status := may_throw(user_exception)
;
- check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result)
+ check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, GoalInfo,
+ !Result)
).
:- pred check_goal_for_exceptions_2(scc::in, module_info::in, vartypes::in,
- hlds_goal_expr::in, proc_result::in, proc_result::out) is det.
+ hlds_goal_expr::in, hlds_goal_info::in, proc_result::in, proc_result::out)
+ is det.
-check_goal_for_exceptions_2(_, _, _, unify(_, _, _, Kind, _), !Result) :-
+check_goal_for_exceptions_2(_, _, _, Goal, _, !Result) :-
+ Goal = unify(_, _, _, Kind, _),
( Kind = complicated_unify(_, _, _) ->
unexpected(this_file,
"complicated unify during exception analysis.")
;
true
).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
Goal = call(CallPredId, CallProcId, CallArgs, _, _, _),
CallPPId = proc(CallPredId, CallProcId),
module_info_pred_info(ModuleInfo, CallPredId, CallPredInfo),
@@ -323,14 +326,76 @@
check_nonrecursive_call(ModuleInfo, VarTypes, CallPPId, CallArgs,
!Result)
).
-check_goal_for_exceptions_2(_, _, _, generic_call(_,_,_,_), !Result) :-
- !:Result = !.Result ^ status := may_throw(user_exception).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, not(Goal), !Result) :-
+check_goal_for_exceptions_2(_, ModuleInfo, VarTypes, Goal, GoalInfo,
+ !Result) :-
+ Goal = generic_call(Details, Args, _ArgModes, _),
+ (
+ Details = higher_order(Var, _, _, _),
+ ClosureValueMap = goal_info_get_ho_values(GoalInfo),
+ ( ClosureValues = ClosureValueMap ^ elem(Var) ->
+ (
+ get_conditional_closures(ModuleInfo, ClosureValues,
+ Conditional)
+ ->
+ (
+ Conditional = []
+ % The values of the higher-order variable are
+ % all procedures that are known not to throw
+ % exceptions.
+ ;
+ Conditional = [_|_],
+ %
+ % For 'conditional' procedures we need to make
+ % sure that if any type variables are bound at
+ % the generic_call site, then this does not
+ % cause the closure to throw an exception
+ % (because of a user-defined equality or
+ % comparison predicate that throws an
+ % exception.)
+ %
+ % If we can resolve all of the polymorphism at
+ % this generic_call site, then we can reach a
+ % definite conclusion about it.
+ %
+ % If we cannot do so, then we propagate the
+ % 'conditional' status to the current predicate
+ % if all the type variables involved are
+ % universally quantified, or mark it as throwing
+ % an exception if some of them are existentially
+ % quantified.
+ %
+ % XXX This is too conservative but we don't
+ % currently perform a fine-grained enough
+ % analysis of where out-of-line
+ % unifications/comparisons occur to be able to
+ % do better.
+ %
+ check_vars(ModuleInfo, VarTypes, Args, !Result)
+ )
+ ;
+ !:Result = !.Result ^ status := may_throw(user_exception)
+ )
+ ;
+ !:Result = !.Result ^ status := may_throw(user_exception)
+ )
+ ;
+ % XXX We could do better with class methods.
+ Details = class_method(_, _, _, _),
+ !:Result = !.Result ^ status := may_throw(user_exception)
+ ;
+ Details = unsafe_cast
+ ;
+ Details = aditi_builtin(_, _),
+ !:Result = !.Result ^ status := may_throw(user_exception)
+ ).
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, not(Goal), _,
+ !Result) :-
check_goal_for_exceptions(SCC, ModuleInfo, VarTypes, Goal, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _,
+ !Result) :-
Goal = scope(_, ScopeGoal),
check_goal_for_exceptions(SCC, ModuleInfo, VarTypes, ScopeGoal, !Result).
-check_goal_for_exceptions_2(_, _, _, Goal, !Result) :-
+check_goal_for_exceptions_2(_, _, _, Goal, _, !Result) :-
Goal = foreign_proc(Attributes, _, _, _, _, _),
( may_call_mercury(Attributes) = may_call_mercury ->
may_throw_exception(Attributes) = MayThrowException,
@@ -346,22 +411,25 @@
;
true
).
-check_goal_for_exceptions_2(_, _, _, shorthand(_), _, _) :-
+check_goal_for_exceptions_2(_, _, _, shorthand(_), _, _, _) :-
unexpected(this_file,
"shorthand goal encountered during exception analysis.").
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
Goal = switch(_, _, Cases),
CaseGoals = list.map((func(case(_, CaseGoal)) = CaseGoal), Cases),
check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, CaseGoals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, Goal, _, !Result) :-
Goal = if_then_else(_, If, Then, Else),
check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, [If, Then, Else],
!Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, disj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, disj(Goals), _,
+ !Result) :-
check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, par_conj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, par_conj(Goals), _,
+ !Result) :-
check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).
-check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, conj(Goals), !Result) :-
+check_goal_for_exceptions_2(SCC, ModuleInfo, VarTypes, conj(Goals), _,
+ !Result) :-
check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals, !Result).
:- pred check_goals_for_exceptions(scc::in, module_info::in, vartypes::in,
@@ -380,6 +448,37 @@
then true
else check_goals_for_exceptions(SCC, ModuleInfo, VarTypes, Goals,
!Result)
+ ).
+
+%----------------------------------------------------------------------------%
+%
+% Further code to handle higher-order variables
+%
+
+ % Given a list of procedure ids extract those whose exception status
+ % has been set to 'conditional'. Fails if one of the procedures in
+ % the set has an exception status that indicate it may throw an
+ % exception, or if the exception status for a procedure has not yet
+ % been set.
+ %
+:- pred get_conditional_closures(module_info::in, set(pred_proc_id)::in,
+ list(pred_proc_id)::out) is semidet.
+
+get_conditional_closures(ModuleInfo, Closures, Conditionals) :-
+ module_info_exception_info(ModuleInfo, ExceptionInfo),
+ set.fold(get_conditional_closure(ExceptionInfo), Closures,
+ [], Conditionals).
+
+:- pred get_conditional_closure(exception_info::in, pred_proc_id::in,
+ list(pred_proc_id)::in, list(pred_proc_id)::out) is semidet.
+
+get_conditional_closure(ExceptionInfo, PPId, !Conditionals) :-
+ ExceptionInfo ^ elem(PPId) = Status,
+ (
+ Status = conditional,
+ list.cons(PPId, !Conditionals)
+ ;
+ Status = will_not_throw
).
%----------------------------------------------------------------------------%
--------------------------------------------------------------------------
mercury-reviews mailing list
post: mercury-reviews at cs.mu.oz.au
administrative address: owner-mercury-reviews at cs.mu.oz.au
unsubscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: unsubscribe
subscribe: Address: mercury-reviews-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------
More information about the reviews
mailing list