mirror of
https://github.com/Mercury-Language/mercury.git
synced 2025-12-15 13:55:07 +00:00
Estimated hours taken: 0.1 Branches: main compiler/notes/failure.html: compiler/notes/allocation.html: Clarify that these describe the LLDS back-end only.
745 lines
26 KiB
HTML
745 lines
26 KiB
HTML
<html>
|
|
<head>
|
|
<title>
|
|
Management of failure in the code generator
|
|
</title>
|
|
</head>
|
|
|
|
<body
|
|
bgcolor="#ffffff"
|
|
text="#000000"
|
|
>
|
|
|
|
<hr>
|
|
<!-------------------------->
|
|
|
|
This document describes the arrangement
|
|
for handling failure in the LLDS code generator.
|
|
It builds upon allocation.html,
|
|
which describes the mechanism we use
|
|
to preserve the values of variables that may be needed on backtracking
|
|
and the mechanism we use to set up resumption points.
|
|
|
|
<hr>
|
|
<!-------------------------->
|
|
|
|
<p>
|
|
|
|
<h2> RUNTIME DATA STRUCTURES </h2>
|
|
|
|
In the design of the nondet stack,
|
|
two registers point to nondet stack frames.
|
|
|
|
<p>
|
|
|
|
<ul>
|
|
<li>
|
|
The maxfr register always points to the topmost nondet stack frame.
|
|
<p>
|
|
<li>
|
|
While executing a model_non procedure,
|
|
the curfr register points to the ordinary nondet stack frame of that procedure.
|
|
While executing a model_det or model_semi procedure,
|
|
the curfr register points to the nondet stack frame
|
|
of the nearest model_non ancestor of that procedure.
|
|
(In the following, we will consider that any model_det or model_semi procedure
|
|
is effectively part of its nearest model_non caller.)
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
Each ordinary nondet stack frame contains five fixed slots:
|
|
|
|
<p>
|
|
|
|
<ul>
|
|
<li> prevfr, the address of the previous frame on the nondet stack
|
|
<li> redoip, the label to go to when backtracking reaches this frame
|
|
<li> redofr, the value to set curfr to when backtracking through redoip
|
|
<li> succip, the label to go to on success
|
|
<li> succfr, the value to reset curfr to on success (frame of our caller)
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
Ordinary nondet stack frames may also contain the values of variables
|
|
and/or temporary values such as saved trail pointers.
|
|
|
|
<p>
|
|
|
|
All temporary nondet stack frames
|
|
contain the three fixed slots, prevfr, redoip and redofr.
|
|
Temporary frames created by model_det and model_semi procedures
|
|
also have a fourth fixed slot, detfr, which points to the stack frame
|
|
(on the det stack) of the procedure that created them.
|
|
Temporary nondet stack frames
|
|
never contain any slots except these fixed slots.
|
|
Therefore for any given nondet frame its size determines its type.
|
|
If this is three words,
|
|
it is a temporary frame created by a model_non procedure;
|
|
if this is four words,
|
|
it is a temporary frame created by a model_det or model_semi procedure;
|
|
if this is five words or more,
|
|
it is an ordinary frame.
|
|
|
|
<p>
|
|
|
|
The fixed slots prevfr, redoip and redofr
|
|
are present in all nondet stack frames, ordinary and temporary,
|
|
at the same offset and with the same semantics.
|
|
Therefore code that accesses only these fields of a nondet stack frame
|
|
does not need to find out what kind of frame it is.
|
|
|
|
<p>
|
|
|
|
The detfr slot is for the acurate garbage collector;
|
|
it is never used by the backtracking mechanism.
|
|
Therefore in the following we can and will treat all temporary frames the same.
|
|
|
|
<p>
|
|
|
|
The redo() macro is implemented as
|
|
|
|
<p>
|
|
|
|
<pre>
|
|
curfr = redofr_slot(maxfr);
|
|
goto *redoip_slot(maxfr);
|
|
</pre>
|
|
|
|
<p>
|
|
|
|
while the fail() macro discards the top nondet stack frame and then executes
|
|
a redo().
|
|
|
|
<p>
|
|
|
|
It is an invariant that
|
|
curfr will always point to an ordinary nondet stack frame;
|
|
it will never point to a temporary frame.
|
|
|
|
<p>
|
|
|
|
It is an invariant that for any nondet stack frame whose address is frame_addr,
|
|
redofr_slot(frame_addr) == frame_addr whenever curfr == frame_addr. However,
|
|
it is possible that redofr_slot(frame_addr) != frame_addr at other times.
|
|
|
|
<p>
|
|
|
|
One implication of this possibility is that
|
|
at a point in the code where execution may resume after a redo(),
|
|
the code of a procedure can assume that curfr == maxfr at that point
|
|
only if it did not hijack any frames anywhere to the left of that point.
|
|
|
|
<h2> CODE GENERATOR DATA STRUCTURES </h2>
|
|
|
|
The proposed code generator uses two data structures for managing failure:
|
|
the resume point stack and the left context.
|
|
It also consults the option --allow-hijacks
|
|
that says whether hijacks (see below) of nondet stack frames are allowed.
|
|
Grades that call for accurate garbage collection imply --no-allow-hijacks.
|
|
|
|
<h3> The resume point stack </h3>
|
|
|
|
Several kinds of goals want to get control on failure.
|
|
|
|
<p>
|
|
|
|
<ul>
|
|
<li> disjunctions want control when
|
|
a nonlast disjunct or something executed to its right fails
|
|
<li> if-then-elses want control when the condition fails
|
|
<li> negations are a special case of if-then-elses
|
|
<li> commits want control when the committed goal fails
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
These kinds of goals can be nested inside each other.
|
|
|
|
<p>
|
|
|
|
Whenever we are about to generate code for a goal after whose failure we
|
|
want to get back control, we push an entry onto the resume point stack.
|
|
This entry gives the arrangements for the resumption point;
|
|
the details of the arrangement are described in the "Resumption points"
|
|
section of allocation.html.
|
|
|
|
<p>
|
|
|
|
The depth of the resume point stack reflects the depth of nesting of
|
|
goals that want to get control on failure.
|
|
|
|
<p>
|
|
|
|
It is an invariant that the resume point stack is the same before and
|
|
after generating code for a goal.
|
|
|
|
<h3> The left context </h3>
|
|
|
|
When the code generator looks at a goal,
|
|
the left context field of code_info gives information about
|
|
what happened to the left of this goal
|
|
that may affect how we handle failures in this goal.
|
|
|
|
<p>
|
|
|
|
The left context has three subfields.
|
|
|
|
<p>
|
|
|
|
<ul>
|
|
<li>
|
|
The resume_point_known subfield can take on
|
|
one of the two values known and unknown,
|
|
saying whether the code generator knows what label to branch to on failure.
|
|
The answer is yes if there is no nondet code between
|
|
the establishment of the tightest enclosing resume point
|
|
and the start of the current goal.
|
|
|
|
If the resume point is known,
|
|
the compiler state also records whether
|
|
the redoip slot of the top nondet stack frame
|
|
is known to contain the address of this resume point.
|
|
XXX The rest of this document has not been updated
|
|
to reflect how we handle this extra component of the failure handling state.
|
|
|
|
<p>
|
|
|
|
<li>
|
|
The curfr_maxfr subfield can take on
|
|
one of the two values must_be_equal and may_be_different,
|
|
saying whether the code generator knows that
|
|
curfr is guaranteed to be equal to maxfr.
|
|
The answer is yes if all of the following conditions hold:
|
|
|
|
<ul>
|
|
<li>
|
|
we are in a procedure that lives on the nondet stack
|
|
(maxfr and curfr are both set to the address of the procedure's
|
|
stack frame when it is created);
|
|
<li>
|
|
there are no calls to nondet procedures on the path between
|
|
the start of the procedure and the start of the current goal; and
|
|
<li>
|
|
the code generator itself hasn't pushed any temporary nondet
|
|
stack frames (see below).
|
|
</ul>
|
|
|
|
<li>
|
|
The condition_environment subfield can take on
|
|
one of the two values inside_non_condition and not_inside_non_condition,
|
|
saying whether we are inside a model_non goal
|
|
that is the condition of an if-then-else.
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
The resume_point_known subfield is initialized to known in all procedures.
|
|
The curfr_maxfr subfield is initialized to
|
|
must_be_equal in model_non procedures,
|
|
and to may_be_different in other procedures.
|
|
The condition_environment subfield
|
|
is always initialized to not_inside_non_condition.
|
|
|
|
<p>
|
|
|
|
When the code generator processes any nondet goal,
|
|
it must set the resume_point_known subfield to unknown at the end of the goal.
|
|
|
|
<p>
|
|
|
|
When the code generator processes a model_non construct
|
|
that leaves this procedure (i.e. a call, higher order call or method call),
|
|
it must also set the curfr_maxfr subfield to may_be_different
|
|
at the end of the goal.
|
|
(Since a model_non procedure can remove its own stack frame
|
|
just before it succeeds for the last time,
|
|
we can't say that after a call to such a procedure
|
|
maxfr *will* be different from curfr.
|
|
|
|
<p>
|
|
|
|
When the code generator processes branched structures (if-then-elses,
|
|
switches or disjunctions), if resume_point_known is unknown at the end of
|
|
any arm, it must be unknown after the branched structure, and if curfr_maxfr
|
|
is may_be_different at the end of any arm, it must also be may_be_different
|
|
after the branched structure.
|
|
|
|
<p>
|
|
|
|
When the code generator establishes a new resumption point,
|
|
it will set the resume_point_known subfield to known,
|
|
but it must not set the curfr_maxfr subfield to must_be_equal.
|
|
|
|
<p>
|
|
|
|
Note that in the absence of model_non code, the resume_point_known subfield
|
|
will always have the value known.
|
|
|
|
<h3> Generating failure </h3>
|
|
|
|
How we generate code for failure depends on the top entry on the resume
|
|
point stack and the left context.
|
|
|
|
<p>
|
|
|
|
If resume_point_known is no, the code we generate for failure is redo().
|
|
If resume_point_known is yes, the code we generate for failure is a branch
|
|
to one of the labels in the resumption point; which one depends on the
|
|
locations of the variables needed at the resumption point
|
|
(see allocation.html).
|
|
|
|
<p>
|
|
|
|
To prepare for backtracking to a resumption point that takes place via
|
|
a redo(), not via a direct branch, we must select one of the labels
|
|
as the one whose address will be put in the redoip slot via which backtracking
|
|
will take place. This label will be the stack label, the label whose
|
|
resume map maps all the resume variables to their stack slots.
|
|
|
|
<p>
|
|
|
|
We have to make sure that this choice is always valid.
|
|
We do this by enforcing three invariants:
|
|
|
|
<ul>
|
|
<li>
|
|
We never generate redo() for failure if resume_point_known is known,
|
|
and we always generate redo() for failure if resume_point_known is unknown.
|
|
|
|
<p>
|
|
|
|
<li>
|
|
At all points when resume_point_known goes from known to unknown,
|
|
we make sure that the resume variables are flushed to their stack slots.
|
|
|
|
<p>
|
|
|
|
<li>
|
|
If, while a resumption point is active,
|
|
control can reach a point where resume_point_known goes from known to unknown,
|
|
liveness.m ensures that the resumption point includes a stack label.
|
|
</ul>
|
|
|
|
<h3> Classifying left contexts </h3>
|
|
|
|
The code generator classifies left contexts
|
|
into those in which hijacking is allowed,
|
|
and those in which hijacking is not allowed.
|
|
Hijacking is allowed only if the --allow-hijacks option is set,
|
|
and the condition_environment subfield is not_inside_non_condition.
|
|
|
|
<p>
|
|
|
|
The code generator further classifies left contexts
|
|
in which hijacking is allowed into the following three categories:
|
|
|
|
<p>
|
|
|
|
<ul>
|
|
<li> best case:
|
|
resume_point_known is known and curfr_maxfr is must_be_equal
|
|
<li> middle case:
|
|
resume_point_known is unknown and curfr_maxfr is must_be_equal
|
|
<li> worst case:
|
|
curfr_maxfr is may_be_different
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
The code generator can treat any situation as a no-hijack case,
|
|
it can treat best case as either middle case or worst case,
|
|
and it can treat middle case as worst case.
|
|
However, it should be able to generate better code
|
|
if it exploits the available information.
|
|
|
|
<p>
|
|
|
|
It may be that no-hijack case yields better code than worst case;
|
|
this should be investigated XXX.
|
|
|
|
<h2> HANDLING DISJUNCTIONS </h2>
|
|
|
|
<h3> Handling nondet disjunctions </h3>
|
|
|
|
<ul>
|
|
<li> No-hijack case.
|
|
Before entering the disjunction,
|
|
the code generator creates a temporary nondet stack frame.
|
|
When the code generator enters a nonlast disjunct,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the next disjunct,
|
|
and sets the redoip slot of the top frame (which is the temporary frame)
|
|
to the stack continuation in that resume point.
|
|
When the code generator enters the last disjunct,
|
|
it does not push any entry on the resume point stack,
|
|
and sets maxfr to the value
|
|
in the prevfr slot of the top frame (which is the temporary frame),
|
|
which pops the temporary frame off the stack.
|
|
|
|
<li> Best case.
|
|
When the code generator enters a nonlast disjunct,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the next disjunct,
|
|
and sets the redoip slot of the top frame (which is this frame)
|
|
to the stack continuation in that resume point.
|
|
When the code generator enters the last disjunct,
|
|
it does not push any entry on the resume point stack,
|
|
and sets the redoip slot of the top frame (which is this frame)
|
|
to the main continuation of the resume point
|
|
that was initially on top of the stack.
|
|
In this case, the disjunction hijacks its own frame
|
|
without having to save the value in the hijacked slot;
|
|
we call this a quarter hijack,
|
|
since it involves one restore.
|
|
|
|
<p>
|
|
|
|
<li> Middle case.
|
|
Before entering the disjunction,
|
|
the code generator saves in a stack slot
|
|
the contents of the redoip slot of the top frame
|
|
(which is this frame, and whose redofr slot must point to this frame).
|
|
When the code generator enters a nonlast disjunct,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the next disjunct,
|
|
and sets the redoip slot of the top frame (which is this frame)
|
|
to the stack continuation in that resume point.
|
|
When the code generator enters the last disjunct,
|
|
it does not push any entry on the resume point stack,
|
|
and sets the redoip slot of the top frame (which is this frame)
|
|
to its old, saved value.
|
|
In this case, the disjunction hijacks its own frame
|
|
while having to save the value in the hijacked slot;
|
|
we call this a half hijack,
|
|
since it involves one save and one restore.
|
|
|
|
<p>
|
|
|
|
<li> Worst case.
|
|
Before entering the disjunction,
|
|
the code generator saves in two stack slots
|
|
the contents of the redoip and redofr slots of the top frame,
|
|
(which may be different from this frame),
|
|
and sets the redofr slot of the top frame from curfr,
|
|
so that backtracking through the redoip slot of that frame
|
|
will set curfr to point to this frame.
|
|
When the code generator enters a nonlast disjunct,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the next disjunct,
|
|
and sets the redoip slot of the top frame
|
|
to the stack continuation in that resume point.
|
|
When the code generator enters the last disjunct,
|
|
it does not push any entry on the resume point stack,
|
|
and restores the redoip and redofr slots of the top frame
|
|
(which may be different from this frame),
|
|
to their old, saved values.
|
|
In this case, the disjunction hijacks a frame that may not be its own;
|
|
we call this a full hijack,
|
|
since it involves two saves and two restores.
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
For all the hijacking cases, the choices represented by the hijacked
|
|
redoip and maybe redofr slots cannot be backtracked to until the disjunction
|
|
that we are generating code for has failed. This cannot happen until the
|
|
last disjunct has failed, by which time the slots must have been restored.
|
|
This maintains the invariant that for any nondet stack frame whose address
|
|
is frame_addr, redofr_slot(frame_addr) == frame_addr whenever curfr ==
|
|
frame_addr.
|
|
|
|
<h3> Handling semi or det disjunctions </h3>
|
|
|
|
When the code generator enters a nonlast disjunct,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the next disjunct.
|
|
When the code generator enters the last disjunct,
|
|
it does not push any entry on the resume point stack.
|
|
|
|
<h2> HANDLING IF-THEN-ELSES </h2>
|
|
|
|
<h3> Handling nondet if-then-elses with nondet conditions </h3>
|
|
|
|
<ul>
|
|
<li> No-hijack case.
|
|
Before the code generator enters the condition,
|
|
the code generator creates a temporary nondet stack frame,
|
|
and saves the address of this temporary frame in a stack slot.
|
|
It also pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the else part,
|
|
and sets the redoip slot of the top frame (which must be the temporary frame)
|
|
to the stack continuation in that resume point.
|
|
After the code generator emerges from the condition,
|
|
it pops the new entry off the resume point stack.
|
|
Before entry to the then part or to the else part,
|
|
it assigns do_fail to the redoip slot of the temporary frame
|
|
(pointed to by the stack slot), effectively removing the temporary frame.
|
|
(We cannot generate code to pop the frame off the stack,
|
|
since it may not be on top.)
|
|
|
|
<li> Best case.
|
|
Before the code generator enters the condition,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the else part,
|
|
and sets the redoip slot of the top frame (which must be this frame)
|
|
to the stack continuation in that resume point.
|
|
After the code generator emerges from the condition,
|
|
it pops the new entry off the resume point stack.
|
|
Before entry to the then part or to the else part,
|
|
it resets the redoip slot of the frame it hijacked (pointed to by curfr)
|
|
to the stack label of the exposed top entry of the resume point stack.
|
|
If that entry has no stack label, then that resume point will never
|
|
be reached via redo() and thus we do not need to reset the redoip slot.
|
|
|
|
<p>
|
|
|
|
<li> Middle case.
|
|
Before entering the if-then-else,
|
|
the code generator saves in a stack slot
|
|
the contents of the redoip of the top frame (which must be this frame).
|
|
Before the code generator enters the condition,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the else part,
|
|
and sets the redoip slot of the top frame
|
|
to the stack continuation in that resume point.
|
|
After the code generator emerges from the condition,
|
|
it pops the new entry off the resume point stack.
|
|
Before entry to the then part or to the else part,
|
|
it resets the redoip slot of the frame it hijacked (pointed to by curfr)
|
|
to its saved value.
|
|
|
|
<p>
|
|
|
|
<li> Worst case.
|
|
Before entering the if-then-else,
|
|
the code generator saves in three stack slots
|
|
the address of the top frame (which is in maxfr),
|
|
and the contents of the redoip and redofr slots of the top frame
|
|
(which may be different from this frame).
|
|
It also sets the redofr slot of the top frame from curfr,
|
|
so that backtracking through the redoip slot of that frame
|
|
will set curfr to point to this frame.
|
|
Before the code generator enters the condition,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the else part,
|
|
and sets the redoip slot of the top frame
|
|
to the stack continuation in that resume point.
|
|
After the code generator emerges from the condition,
|
|
it pops the new entry off the resume point stack.
|
|
Before entry to the then part or to the else part,
|
|
it resets the redoip and redofr slots
|
|
of the frame it hijacked to their saved values.
|
|
Since maxfr may have been changed by the condition pushing new frames,
|
|
the address of the frame in which the restoration is to be performed
|
|
is given by the saved original value of maxfr
|
|
(although on entry to the else part maxfr will be equal to its value
|
|
on entry to the if-then-else, which may be different from curfr).
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
All these four manipulate the redoip slot of the nondet stack frame
|
|
that is on top of the nondet stack when execution enters the condition.
|
|
On entry to the condition, we set it to point the start of the else part;
|
|
on exit from the condition, we reset it to some other value to prevent
|
|
backtracking to the else part.
|
|
If the condition hijacks this slot, this scheme will not work.
|
|
(Although the code that hijacks the slot
|
|
will restore it to its original value before finally failing,
|
|
that is too late in this case.)
|
|
Therefore before entering the condition,
|
|
the code generator sets condition_environment to inside_non_condition;
|
|
on exit from the condition, it restores the original value.
|
|
(These are the only times the value of condition_environment is ever changed.)
|
|
|
|
<p>
|
|
|
|
Before entering the condition, the code generator will set resume_point_known
|
|
to known, since the resumption point to go to on failure of the condition is
|
|
known (it is the start of the else part). However, this resumption point
|
|
does not apply to failures in the then part. Therefore before entering the
|
|
then part, the code generator will reset resume_point_known to the value
|
|
it had before entering the if-then-else, except if resume_point_known
|
|
has become unknown during the generation of code for the condition, in which
|
|
case it will continue to be unknown.
|
|
|
|
<p>
|
|
|
|
A failure inside the condition should cause
|
|
a branch to the start of the else case
|
|
only if the condition has not already succeeded.
|
|
In general, the code inside the condition
|
|
may branch directly to the start of the else case
|
|
only up to the first goal that can succeed more than once;
|
|
any other goal following such a goal should branch through the redoip slot
|
|
on which the if-then-else performs the soft cut.
|
|
There are only two kinds of constructs that can succeed more than once:
|
|
calls (including higher order and method calls) and disjunctions.
|
|
When the code generator processes a call that can succeed more than once,
|
|
it sets resume_point_known to unknown,
|
|
which causes later failures to perform a redo.
|
|
To ensure that code in the last disjunct of a model_non disjunction
|
|
and code following a model_non disjunction also fail via a redo,
|
|
the code generator will,
|
|
on entering the last disjunct of a model_non disjunction,
|
|
check whether condition_environment is inside_non_condition,
|
|
and if yes, it will set resume_point_known to unknown.
|
|
(We do not do this in disjuncts other than the last
|
|
because non-last disjuncts fail to the start of the next disjunct).
|
|
Since at the end of branched control structures such as disjunctions
|
|
the code generator sets resume_point_known to unknown
|
|
if resume_point_known was unknown at the end of any branch,
|
|
this ensures that resume_point_known will stay unknown after the disjunction.
|
|
(The end of branched control structures
|
|
treat the curfr_vs_maxfr subfield similarly, i.e. set it to may_be_different
|
|
if it was may_be_different at the end of any branch.
|
|
The condition_environment subfield must, by construction,
|
|
have the same value at the end of every branch,
|
|
and this is the value it will have after the branched control structure.)
|
|
|
|
<h3> Handling nondet if-then-elses with semi or det conditions </h3>
|
|
|
|
We can handle these as we handle nondet if-then-elses with nondet conditions,
|
|
or we can handle them as we handle semi or det if-then-elses, with the
|
|
exception that we generate the then part and the else part as nondet goals.
|
|
The latter is more efficient, so that is what we do.
|
|
|
|
<h3> Handling semi or det if-then-elses </h3>
|
|
|
|
Before the code generator enters the condition,
|
|
it pushes a new entry on the resume point stack
|
|
indicating the resume point at the start of the else part,
|
|
to the stack continuation in that resume point.
|
|
After the code generator emerges from the condition,
|
|
it pops the new entry off the resume point stack.
|
|
|
|
<h2> HANDLING NEGATIONS </h2>
|
|
|
|
We handle not(G) as (G -> fail ; true).
|
|
|
|
<h2> HANDLING COMMITS </h2>
|
|
|
|
<h3> Handling commits across nondet goals </h3>
|
|
|
|
<ul>
|
|
<li> No-hijack case.
|
|
Before the code generator enters a goal that is being committed across,
|
|
it saves the value of maxfr in a stack slot,
|
|
and then it creates a temporary nondet stack frame
|
|
whose redoip slot points to the resume point
|
|
for getting control back if the goal has no solutions.
|
|
It also pushes a new entry on the resume point stack
|
|
indicating this resume point.
|
|
The code at this resume point will perform a failure in the manner
|
|
appropriate for whatever entry was originally on top of the resume point stack.
|
|
Both continuations will reset maxfr
|
|
to the value it had originally, which is in that stack slot.
|
|
|
|
<li> Best case.
|
|
Before the code generator enters a goal that is being committed across,
|
|
it pushes a new entry on the resume point stack indicating
|
|
the resume point for getting control back if the goal has no solutions
|
|
and sets the redoip slot of the top frame (which is this frame)
|
|
to the stack continuation in that resume point.
|
|
The code at this resume point will perform a failure in the manner
|
|
appropriate for whatever entry was originally on top of the resume point stack.
|
|
The code after the goal in the success continuation
|
|
will reset maxfr to the value it had originally, which is in curfr.
|
|
Both continuations will reset the redoip slot of the current frame
|
|
to its original value,
|
|
which is the address of the stack label
|
|
in the originally top failure continuation.
|
|
|
|
<p>
|
|
|
|
<li> Middle case.
|
|
Before the code generator enters a goal that is being committed across,
|
|
it pushes a new entry on the resume point stack indicating
|
|
the resume point for getting control back if the goal has no solutions,
|
|
saves the value of the redoip slot of the top frame (which is this frame)
|
|
and overrides this redoip slot to make it point
|
|
to the stack continuation in the resume point.
|
|
The success continuation will reset maxfr to curfr;
|
|
the failure continuation doesn't have to
|
|
(since the resume point can only be reached
|
|
if all other frames above the one hijacked have failed.)
|
|
Both continuations will reset the redoip slot of the top frame
|
|
to the saved value.
|
|
The failure continuation will then perform a failure in the manner
|
|
appropriate for the values of the resume point stack and left context
|
|
that were current before the commit.
|
|
The order of actions in the success continuation is important,
|
|
since resetting maxfr affects which frame is top.
|
|
|
|
<p>
|
|
|
|
<li> Worst case.
|
|
Before the code generator enters a goal that is being committed across,
|
|
it saves the value of maxfr,
|
|
pushes a new entry on the resume point stack indicating
|
|
the resume point for getting control back if the goal has no solutions,
|
|
saves the values of the redoip and redofr slots of the top frame
|
|
(which may be different from this frame),
|
|
overrides the redoip slot to make it point
|
|
to the stack continuation in the resume point
|
|
and the redofr slot to point to this frame.
|
|
The success continuation will reset maxfr;
|
|
the failure continuation doesn't have to
|
|
(since the resume point can only be reached
|
|
if all other frames above the one hijacked have failed.)
|
|
Both continuations will reset the redoip and redofr slots of the top frame
|
|
to their saved values.
|
|
The failure continuation will then perform a failure in the manner
|
|
appropriate for the values of the resume point stack and left context
|
|
that were current before the commit.
|
|
The order of actions in the success continuation is important,
|
|
since resetting maxfr affects which frame is top.
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
Since we are cutting away any frames created by the goal being committed
|
|
across, and since any resumption points established in that goal cannot
|
|
survive across the commit, at the end of processing the commit we can reset
|
|
both curfr_maxfr and resume_point_known to their values before the commit.
|
|
|
|
<p>
|
|
|
|
We do not need to push a junk frame
|
|
when entering the goal to be committed across
|
|
even if this goal may perform a hijacking.
|
|
If the goal fails, either it did not do any hijacking,
|
|
or it will have restored anything it hijacked before failing.
|
|
If the goal succeeds, it may have hijacked the top frame,
|
|
but we since we do not need to preserve any further solutions inside the goal,
|
|
we can simply restore that frame to its original contents anyway.
|
|
|
|
<h3> Handling commits across multi goals </h3>
|
|
|
|
<ul>
|
|
<li>
|
|
If curfr_maxfr is must_be_equal,
|
|
the code after the goal will reset maxfr to curfr.
|
|
<li>
|
|
If curfr_maxfr is may_be_different,
|
|
then before the code generator enters a goal that is being committed across,
|
|
it saves the value of maxfr in a stack slot.
|
|
The code after the goal will reset maxfr to the saved value.
|
|
</ul>
|
|
|
|
<p>
|
|
|
|
This is the way the code generator handles commits across nondet goals except
|
|
that we do not need to handle failure of the goal being committed across,
|
|
and thus do not need to set any redoips or hijack anything.
|
|
|
|
<p>
|
|
|
|
<hr>
|
|
<!-------------------------->
|
|
Last update was $Date: 2003-10-23 06:06:30 $ by $Author: fjh $@cs.mu.oz.au. <br>
|
|
</body>
|
|
</html>
|