Commit 56a05294 authored by Simon Peyton Jones's avatar Simon Peyton Jones
Browse files

Add comments about the meaning of can_fail and has_side_effects

Taken from Trac #5658
parent 88cd0d1f
......@@ -285,7 +285,7 @@ code generator will fall over if they aren't satisfied.
%************************************************************************
%* *
\subsubsection[PrimOp-ool]{Which PrimOps are out-of-line}
Which PrimOps are out-of-line
%* *
%************************************************************************
......@@ -299,15 +299,39 @@ primOpOutOfLine :: PrimOp -> Bool
\end{code}
primOpOkForSpeculation
~~~~~~~~~~~~~~~~~~~~~~
%************************************************************************
%* *
Failure and side effects
%* *
%************************************************************************
Note [PrimOp can_fail and has_side_effects]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* A primop that is neither can_fail nor has_side_effects can be
executed speculatively, any number of times
* A primop that is marked can_fail cannot be executed speculatively,
(becuase the might provoke the failure), but it can be repeated.
Why would you want to do that? Perhaps it might enable some
eta-expansion, if you can prove that the lambda is definitely
applied at least once. I guess we don't currently do that.
* A primop that is marked has_side_effects can be neither speculated
nor repeated; it must be executed exactly the right number of
times.
So has_side_effects implies can_fail. We don't currently exploit
the case of primops that can_fail but do not have_side_effects.
Note [primOpOkForSpeculation]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Sometimes we may choose to execute a PrimOp even though it isn't
certain that its result will be required; ie execute them
``speculatively''. The same thing as ``cheap eagerness.'' Usually
this is OK, because PrimOps are usually cheap, but it isn't OK for
(a)~expensive PrimOps and (b)~PrimOps which can fail.
PrimOps that have side effects also should not be executed speculatively.
* PrimOps that are expensive
* PrimOps which can fail
* PrimOps that have side effects
Ok-for-speculation also means that it's ok *not* to execute the
primop. For example
......@@ -318,10 +342,53 @@ that has side effects mustn't be dicarded in this way, of course!
See also @primOpIsCheap@ (below).
Note [primOpHasSideEffects]
~~~~~~~~~~~~~~~~~~~~~~~~~~~
Some primops have side-effects and so, for example, must not be
duplicated.
This predicate means a little more than just "modifies the state of
the world". What it really means is "it cosumes the state on its
input". To see what this means, consider
let
t = case readMutVar# v s0 of (# s1, x #) -> (S# s1, x)
y = case t of (s,x) -> x
in
... y ... y ...
Now, this is part of an ST or IO thread, so we are guaranteed by
construction that the program uses the state in a single-threaded way.
Whenever the state resulting from the readMutVar# is demanded, the
readMutVar# will be performed, and it will be ordered correctly with
respect to other operations in the monad.
But there's another way this could go wrong: GHC can inline t into y,
and inline y. Then although the original readMutVar# will still be
correctly ordered with respect to the other operations, there will be
one or more extra readMutVar#s performed later, possibly out-of-order.
This really happened; see #3207.
The property we need to capture about readMutVar# is that it consumes
the State# value on its input. We must retain the linearity of the
State#.
Our fix for this is to declare any primop that must be used linearly
as having side-effects. When primOpHasSideEffects is True,
primOpOkForSpeculation will be False, and hence primOpIsCheap will
also be False, and applications of the primop will never be
duplicated.
\begin{code}
primOpHasSideEffects :: PrimOp -> Bool
#include "primop-has-side-effects.hs-incl"
primOpCanFail :: PrimOp -> Bool
#include "primop-can-fail.hs-incl"
primOpOkForSpeculation :: PrimOp -> Bool
-- See comments with CoreUtils.exprOkForSpeculation
-- See Note [primOpOkForSpeculation]
-- See comments with CoreUtils.exprOkForSpeculation
primOpOkForSpeculation op
= not (primOpHasSideEffects op || primOpOutOfLine op || primOpCanFail op)
\end{code}
......@@ -356,6 +423,13 @@ primOpIsCheap op = primOpOkForSpeculation op
-- even if primOpIsCheap sometimes says 'True'.
\end{code}
%************************************************************************
%* *
PrimOp code size
%* *
%************************************************************************
primOpCodeSize
~~~~~~~~~~~~~~
Gives an indication of the code size of a primop, for the purposes of
......@@ -374,50 +448,12 @@ primOpCodeSizeForeignCall :: Int
primOpCodeSizeForeignCall = 4
\end{code}
\begin{code}
primOpCanFail :: PrimOp -> Bool
#include "primop-can-fail.hs-incl"
\end{code}
And some primops have side-effects and so, for example, must not be
duplicated.
This predicate means a little more than just "modifies the state of
the world". What it really means is "it cosumes the state on its
input". To see what this means, consider
let
t = case readMutVar# v s0 of (# s1, x #) -> (S# s1, x)
y = case t of (s,x) -> x
in
... y ... y ...
Now, this is part of an ST or IO thread, so we are guaranteed by
construction that the program uses the state in a single-threaded way.
Whenever the state resulting from the readMutVar# is demanded, the
readMutVar# will be performed, and it will be ordered correctly with
respect to other operations in the monad.
But there's another way this could go wrong: GHC can inline t into y,
and inline y. Then although the original readMutVar# will still be
correctly ordered with respect to the other operations, there will be
one or more extra readMutVar#s performed later, possibly out-of-order.
This really happened; see #3207.
The property we need to capture about readMutVar# is that it consumes
the State# value on its input. We must retain the linearity of the
State#.
Our fix for this is to declare any primop that must be used linearly
as having side-effects. When primOpHasSideEffects is True,
primOpOkForSpeculation will be False, and hence primOpIsCheap will
also be False, and applications of the primop will never be
duplicated.
\begin{code}
primOpHasSideEffects :: PrimOp -> Bool
#include "primop-has-side-effects.hs-incl"
\end{code}
%************************************************************************
%* *
PrimOp types
%* *
%************************************************************************
\begin{code}
primOpType :: PrimOp -> Type -- you may want to use primOpSig instead
......
......@@ -41,12 +41,13 @@
defaults
has_side_effects = False
out_of_line = False
out_of_line = False -- See Note Note [PrimOp can_fail and has_side_effects] in PrimOp
can_fail = False -- See Note Note [PrimOp can_fail and has_side_effects] in PrimOp
commutable = False
code_size = { primOpCodeSizeDefault }
can_fail = False
strictness = { \ arity -> mkStrictSig (mkTopDmdType (replicate arity lazyDmd) TopRes) }
-- Currently, documentation is produced using latex, so contents of
-- description fields should be legal latex. Descriptions can contain
-- matched pairs of embedded curly brackets.
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment