[m-rev.] for review: self-tail-calls in MLDS code generator

Peter Wang novalazy at gmail.com
Mon Aug 7 22:56:56 AEST 2017


On Sat, 05 Aug 2017 13:43:14 +0200 (CEST), "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> Implement self-tail-call optimization in the MLDS code generator.
> 
> This is a step towards implementing not just self- but also mutual
> tail recursion in the MLDS code generator.
> 

> diff --git a/compiler/mark_tail_calls.m b/compiler/mark_tail_calls.m
> index 102e9c6..96c356f 100644
> --- a/compiler/mark_tail_calls.m
> +++ b/compiler/mark_tail_calls.m
> @@ -97,6 +108,47 @@
>  
>  %---------------------------------------------------------------------------%
>  %
> +% These types and predicates are exported for ml_proc_gen.m and ml_call_gen.m.
> +% ml_proc_gen.m sets up the warning parameters, and ml_call_gen.m uses them
> +% when it finds that it even though mark_tail_calls.m has marked a call
> +% as a tail call, it cannot actual implement that call as a tail call.
> +%

Fix that sentence.

> +%---------------------------------------------------------------------------%
> +%
>  % These predicates are exported for ml_tailcall.m; see above for the reason.
>  %
>  
> @@ -122,16 +174,29 @@
>      proc_id::in, simple_call_id::in, warning_or_error::in, prog_context::in,
>      list(error_spec)::in, list(error_spec)::out) is det.
>  
> -    % add_message_for_no_tail_or_nontail_recursive_calls(SimpleCallId, Context,
> -    %   !Specs):
> +    % Have we found any recursive call been found so far?

Fix that sentence.

>      %
> -    % Add warning to !Specs reporting that the procedure described by
> +    % We use this to generate warnings about pragmas that intend to control
> +    % how recursive calls that are not *tail* recursive should be treated,
> +    % when the procedure they are about contains no recursive calls at all,
> +    % either self-recursive or (if we have SCC information) mutually recursive.
> +    %
> +:- type found_any_rec_calls
> +    --->    not_found_any_rec_calls
> +    ;       found_any_rec_calls.
> +
> +    % maybe_report_no_tail_or_nontail_recursive_calls(PredInfo, ProcInfo
> +    %   FoundAnyRecCalls, Context, !Specs):
> +    %
> +    % If FoundAnyRecCalls = not_found_any_rec_calls but ProcInfo
> +    % say the procedure has a pragma about tail recursive calls on it,

says

> +    % then add a message to !Specs reporting that the procedure described by
>      % SimpleCallId contains no recursive calls at all, tail-recursive or

> @@ -857,13 +908,33 @@ mark_tail_rec_calls_in_goal(Goal0, Goal, AtTail0, AtTail, !Info) :-
>          GoalExpr = conj(ConjType, Goals),
>          Goal = hlds_goal(GoalExpr, GoalInfo0)
>      ;
> -        GoalExpr0 = disj(Disjs0),
> -        project_seen_later_rec_call(AtTail0, SeenLaterRecCall0),
> -        list.map_foldl2(mark_tail_rec_calls_in_disj(AtTail0), Disjs0, Disjs,
> -            SeenLaterRecCall0, SeenLaterRecCall, !Info),
> -        AtTail = not_at_tail(SeenLaterRecCall),
> -        GoalExpr = disj(Disjs),
> -        Goal = hlds_goal(GoalExpr, GoalInfo0)
> +        GoalExpr0 = disj(Disjuncts0),
> +        ( if list.split_last(Disjuncts0, NonLastDisjuncts0, LastDisjunct0) then
> +            % If the disjunction is in tail position, then it is possible
> +            % for a goal inside the last disjunct to be a tail call.
> +            mark_tail_rec_calls_in_goal(LastDisjunct0, LastDisjunct,
> +                AtTail0, LastAtTail, !Info),
> +            % Even if the disjunction as a whole is in tail position,
> +            % a goal inside a nonlast disjunct cannot be a tail call,
> +            % because if it fails, its execution will be followed
> +            % by backtracking to later disjuncts.
> +            project_seen_later_rec_call(LastAtTail, SeenLaterRecCall0),
> +            NonLastAtTail0 = not_at_tail(SeenLaterRecCall0),
> +            list.map_foldl2(
> +                mark_tail_rec_calls_in_nonlast_disjunct(NonLastAtTail0),
> +                NonLastDisjuncts0, NonLastDisjuncts,
> +                SeenLaterRecCall0, SeenLaterRecCall, !Info),
> +            AtTail = not_at_tail(SeenLaterRecCall),
> +            GoalExpr = disj(NonLastDisjuncts ++ [LastDisjunct]),
> +            Goal = hlds_goal(GoalExpr, GoalInfo0)
> +        else
> +            % There are no disjuncts. Any goals before the disjunct

disjunction

> +            % will be followed by disj([]), which is `fail', so they cannot
> +            % be tail calls.
> +            project_seen_later_rec_call(AtTail0, SeenLaterRecCall),
> +            AtTail = not_at_tail(SeenLaterRecCall),
> +            Goal = Goal0
> +        )
>      ;
>          GoalExpr0 = switch(Var, CanFail, Cases0),
>          project_seen_later_rec_call(AtTail0, SeenLaterRecCall0),

> diff --git a/compiler/ml_optimize.m b/compiler/ml_optimize.m
> index 5c19ae8..a8b63d2 100644
> --- a/compiler/ml_optimize.m
> +++ b/compiler/ml_optimize.m
> @@ -35,9 +35,61 @@
>  :- import_module libs.
>  :- import_module libs.globals.
>  :- import_module ml_backend.mlds.
> +:- import_module parse_tree.
> +:- import_module parse_tree.prog_data.
> +
> +:- import_module list.
>  
>  :- pred mlds_optimize(globals::in, mlds::in, mlds::out) is det.
>  
> +    % XXX Temporarily exported to ml_call_gen.m. When tail recursion
> +    % optimization is moved completely into the code generator,
> +    % this whole predicate will be moved to ml_call_gen.m.
> +    %
> +    % Assign the given list of rvals (the actual parameter) to the given list
> +    % of mlds_arguments (the formal parameter). This is used as part of tail
> +    % recursion optimization (see above).
> +    %
> +    % For each actual parameter that differs from its corresponding formal
> +    % parameter, we declare a temporary variable to hold the next value
> +    % of the formal parameter, and assign it the value of the actual parameter.
> +    % Once this has been done for all parameters, we then assign each formal
> +    % parameter its next value. The code we generate looks like this:
> +    %
> +    %   % These are returned in TempDefns.
> +    %   SomeType new_value_of_arg1;
> +    %   SomeType new_value_of_arg3;
> +    %   SomeType new_value_of_arg3;
> +    %
> +    %   % These are returned in InitStmts.
> +    %   new_value_of_arg1 = <the a new value of parameter 1>
> +    %   new_value_of_arg2 = <the a new value of parameter 2>
> +    %   new_value_of_arg3 = <the a new value of parameter 3>

the a

That looks fine, otherwise.

Peter


More information about the reviews mailing list