From befef2343cb1aded4172df800f72453eb5695b79 Mon Sep 17 00:00:00 2001 From: Simon Peyton Jones <simonpj@microsoft.com> Date: Mon, 12 Dec 2011 11:16:49 +0000 Subject: [PATCH] Add comments about the meaning of can_fail and has_side_effects Taken from Trac #5658 --- compiler/prelude/PrimOp.lhs | 136 ++++++++++++++++++++------------ compiler/prelude/primops.txt.pp | 5 +- 2 files changed, 89 insertions(+), 52 deletions(-) diff --git a/compiler/prelude/PrimOp.lhs b/compiler/prelude/PrimOp.lhs index ec5c09611ebd..d57d1f926ed6 100644 --- a/compiler/prelude/PrimOp.lhs +++ b/compiler/prelude/PrimOp.lhs @@ -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 diff --git a/compiler/prelude/primops.txt.pp b/compiler/prelude/primops.txt.pp index a695344225a2..5047b3cb631e 100644 --- a/compiler/prelude/primops.txt.pp +++ b/compiler/prelude/primops.txt.pp @@ -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. -- GitLab