[m-rev.] for review: disable_warnings scope
paul at bone.id.au
Wed Jan 11 13:44:02 AEDT 2017
On Tue, Jan 10, 2017 at 12:38:47AM +1100, Zoltan Somogyi wrote:
> On Mon, 9 Jan 2017 17:08:39 +1100, Paul Bone <paul at bone.id.au> wrote:
> > On Mon, Jan 09, 2017 at 12:46:32AM +1100, Zoltan Somogyi wrote:
> > If I remember completely.
> > LLDS:
> > Tail calls are identified in an HLDS stage,
> > Warnings are generated in (the same?) HLDS stage.
> > TCO is performed during HLDS->LLDS conversion.
> No, that is not quite right.
> Traditionally, the LLDS code generator didn't know which calls are tail calls;
> it was only the optimizer that turned a call whose return label was the
> procedure epilogue into a tail call. That changed *very* slightly when
> I implemented (a) marking calls as tail calls for the debugger, and (b)
> suppressing tail call optimization for use in cases where the stack frame
> may still be needed by parallel conjuncts.
Ah, that explaination helps.
> > MLDS:
> > HLDS->MLDS conversion occurs.
> > Tail calls are identified _and_ optimised in one MLDS stage.
> > Warnings are generated in (the same?) MLDS stage.
> > If we wanted to unify these parts of the compiler we'd probably need to
> > re-implement most of the MLDS tail call code.
> What MLDS tail call code? And why?
Any code that "decides" which is a tail call and which is not. I beleive it
is better if one module decide what is a tail call and other modules act on
this decision (via the mark in the HLDS for example). The code that makes
that decision for MLDS grades would need to be moved to the the
> > If we decide want to do
> > that, it should probably be done at the same time as my MLDS mutual
> > recursion work.
> Would you mind filling me on that?
The whole thread is worth reading.
> > Another option is to find whatever common code (should) exist between them
> > and make that shared but otherwise keep them separate. Then we will need to
> > pass the ignore warnings scope information through to the MLDS backend.
> I don't see any need for that.
> The way things work for the LLDS backend at the moment is:
> - if the user wants warnings for recursive calls that aren't tail recursive,
> we run mark_tail_calls.m to get the warnings;
> - possibly we run some HLDS to HLDS transformations;
> - we generate LLDS code; and
> - while optimizing the LLDS code, we replace some calls with tail calls.
> To make sure that step 1 generates the right warnings,
> - it needs to know what calls the code generator and/or the optimizer
> will turn into tail calls, and
> - it needs to be done late enough that none of the transformations
> done in step 2 change the code in ways that would invalidate
> that knowledge.
Is the decision of what to make a tail call made twice? Or do the later
passes simply follow the marked tailcalls?
> I believe both of those conditions hold. I see no reason why they wouldn't
> hold for the MLDS backend as well. As you point out, the "knowledge
> about the behavior of the optimizer" will depend on which backend's optimizer
> gets invoked, but even here I see no difference beyond the treatment
> of mutually recursive calls.
> Note that if a HLDS transformation (such as last call modulo constructor)
> can affect what calls end up being tail calls, we want to run them BEFORE
> we generate the warnings ANYWAY, just the get warnings that reflect the
> final code accurately.
> > This second option is tempting because it keeps these separate things
> > separate. Different backends have very different needs, even though both
> > need to do TCO.
> What "needs" are you referring to?
Not a very descriptive word. Since LLDS is an aubstract machine and MLDS is
procedural how they do TCO is different.
> > This is my "gut feeling" preference, but I'm beginning to
> > think that if we can handle both backends in mark_tail_calls.m (and generate
> > warnings there) then actually do TCO for each backend separately that may be
> > best. Provided that we make the logic in mark_tail_calls very clear with
> > respect to which backends support which types of TCO. This is currently my
> > favorite option.
> That is what I was proposing from the start.
Sorry, I was trying to say that I agreed with your proposal, and show that I
understood by restating it in my own words. I should have acknowledged you
to make it clear that I thought that this is what you were saying.
> > > diff --git a/compiler/constraint.m b/compiler/constraint.m
> > > index 15b8020..ec89dea 100644
> > > --- a/compiler/constraint.m
> > > +++ b/compiler/constraint.m
> > > @@ -165,6 +165,17 @@ propagate_conj_sub_goal_2(Constraints, Goal0, FinalGoals, !Info) :-
> > > ;
> > > GoalExpr = scope(Reason, SubGoal0),
> > > (
> > > + Reason = disable_warnings(_, _),
> > > + % NOTE: This assumes that compiler passes that generate the
> > > + % warnings that could be disabled by this scope have all been run
> > > + % BEFORE program transformations such as constraint propagation.
> > > + % If they haven't been, then the transformations can hide warnings
> > > + % about code by moving them into these scopes, or can caused them
> > > + % to be generated when the user does not want them by moving
> > > + % the warned-about code out of such scopes.
> > > + propagate_goal(Constraints, SubGoal0, SubGoal, !Info),
> > > + FinalGoals = [hlds_goal(scope(Reason, SubGoal), GoalInfo)]
> > > + ;
> > > ( Reason = exist_quant(_)
> > > ; Reason = from_ground_term(_, from_ground_term_deconstruct)
> > > ; Reason = from_ground_term(_, from_ground_term_other)
> > That will probably be a problem. Both backends process their tailcalls
> > after this stage.
> The comment is talking about which calls *could later* be turned into tail calls.
> It is NOT supposed to be talking about calls which *have* been turned into tail calls,
> since (as I explained about) that happens only in the backends' optimizers.
> What gave you the impression that it did?
I didn't have that impression. I meant to say that your concern about goals
moving across this scope is a valid cnncern.
More information about the reviews