[Git][ghc/ghc][wip/marge_bot_batch_merge_job] 3 commits: Improve documentation around IOException and ioe_filename
Marge Bot (@marge-bot)
gitlab at gitlab.haskell.org
Mon Jul 31 09:00:18 UTC 2023
Marge Bot pushed to branch wip/marge_bot_batch_merge_job at Glasgow Haskell Compiler / GHC
Commits:
42aa7fbd by Julian Ospald at 2023-07-30T17:22:01-04:00
Improve documentation around IOException and ioe_filename
See:
* https://github.com/haskell/core-libraries-committee/issues/189
* https://github.com/haskell/unix/pull/279
* https://github.com/haskell/unix/pull/289
- - - - -
db54e894 by Matthew Craven at 2023-07-31T04:58:53-04:00
Adjust and clarify handling of primop effects
The existing "can_fail" and "has_side_effects" primop attributes that
previously governed this were used in inconsistent and confusingly-
documented ways, especially with regard to raising exceptions. This
patch replaces them with a single "effect" attribute, with four
possible values (NoEffect, CanFail, ThrowsException, ReadWriteEffect)
as described in Note [Classifying primop effects].
A substantial amount of related documentation has been re-drafted for
clarity and accuracy.
In the process of making this attribute format change for literally
every primop, several existing mis-classifications were detected and
corrected.
New primop attributes "cheap" and "work_free" were
also added, and used in the obvious places.
In view of their actual meaning and uses, `primOpOkForSideEffects` and
`exprOkForSideEffects` have been renamed to `primOpOkToDiscard` and
`exprOkToDiscard`, respectively.
- - - - -
2810170d by Sylvain Henry at 2023-07-31T04:59:04-04:00
JS: implement getMonotonicTime (fix #23687)
- - - - -
27 changed files:
- compiler/GHC/Builtin/PrimOps.hs
- compiler/GHC/Builtin/primops.txt.pp
- compiler/GHC/Core.hs
- compiler/GHC/Core/Opt/ConstantFold.hs
- compiler/GHC/Core/Opt/FloatIn.hs
- compiler/GHC/Core/Opt/SetLevels.hs
- compiler/GHC/Core/Opt/Simplify/Iteration.hs
- compiler/GHC/Core/Utils.hs
- compiler/GHC/Types/Demand.hs
- compiler/Setup.hs
- hadrian/src/Rules/Generate.hs
- hadrian/src/Rules/Lint.hs
- hadrian/src/Settings/Builders/GenPrimopCode.hs
- libraries/base/GHC/Clock.hsc
- libraries/base/GHC/Conc/POSIX.hs
- libraries/base/GHC/IO/Exception.hs
- libraries/base/Unsafe/Coerce.hs
- + libraries/base/tests/T23687.hs
- libraries/base/tests/all.T
- + testsuite/tests/ghc-api/PrimOpEffect_Sanity.hs
- testsuite/tests/ghc-api/all.T
- utils/genprimopcode/AccessOps.hs
- utils/genprimopcode/Lexer.x
- utils/genprimopcode/Main.hs
- utils/genprimopcode/Parser.y
- utils/genprimopcode/ParserM.hs
- utils/genprimopcode/Syntax.hs
Changes:
=====================================
compiler/GHC/Builtin/PrimOps.hs
=====================================
@@ -17,10 +17,12 @@ module GHC.Builtin.PrimOps (
tagToEnumKey,
primOpOutOfLine, primOpCodeSize,
- primOpOkForSpeculation, primOpOkForSideEffects,
- primOpIsCheap, primOpFixity, primOpDocs,
+ primOpOkForSpeculation, primOpOkToDiscard,
+ primOpIsWorkFree, primOpIsCheap, primOpFixity, primOpDocs,
primOpIsDiv, primOpIsReallyInline,
+ PrimOpEffect(..), primOpEffect,
+
getPrimOpResultInfo, isComparisonPrimOp, PrimOpResultInfo(..),
PrimCall(..)
@@ -311,221 +313,316 @@ primOpOutOfLine :: PrimOp -> Bool
* *
************************************************************************
-Note [Checking versus non-checking primops]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
- In GHC primops break down into two classes:
-
- a. Checking primops behave, for instance, like division. In this
- case the primop may throw an exception (e.g. division-by-zero)
- and is consequently is marked with the can_fail flag described below.
- The ability to fail comes at the expense of precluding some optimizations.
-
- b. Non-checking primops behavior, for instance, like addition. While
- addition can overflow it does not produce an exception. So can_fail is
- set to False, and we get more optimisation opportunities. But we must
- never throw an exception, so we cannot rewrite to a call to error.
-
- It is important that a non-checking primop never be transformed in a way that
- would cause it to bottom. Doing so would violate Core's let-can-float invariant
- (see Note [Core let-can-float invariant] in GHC.Core) which is critical to
- the simplifier's ability to float without fear of changing program meaning.
-
-
-Note [PrimOp can_fail and has_side_effects]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Both can_fail and has_side_effects mean that the primop has
-some effect that is not captured entirely by its result value.
-
----------- has_side_effects ---------------------
-A primop "has_side_effects" if it has some side effect, visible
-elsewhere, apart from the result it returns
- - reading or writing to the world (I/O)
- - reading or writing to a mutable data structure (writeIORef)
- - throwing a synchronous Haskell exception
-
-Often such primops have a type like
- State -> input -> (State, output)
-so the state token guarantees ordering. In general we rely on
-data dependencies of the state token to enforce write-effect ordering,
-but as the notes below make clear, the matter is a bit more complicated
-than that.
-
- * NB1: if you inline unsafePerformIO, you may end up with
- side-effecting ops whose 'state' output is discarded.
- And programmers may do that by hand; see #9390.
- That is why we (conservatively) do not discard write-effecting
- primops even if both their state and result is discarded.
-
- * NB2: We consider primops, such as raiseIO#, that can raise a
- (Haskell) synchronous exception to "have_side_effects" but not
- "can_fail". We must be careful about not discarding such things;
- see the paper "A semantics for imprecise exceptions".
-
- * NB3: *Read* effects on *mutable* cells (like reading an IORef or a
- MutableArray#) /are/ included. You may find this surprising because it
- doesn't matter if we don't do them, or do them more than once. *Sequencing*
- is maintained by the data dependency of the state token. But see
- "Duplication" below under
- Note [Transformations affected by can_fail and has_side_effects]
-
- Note that read operations on *immutable* values (like indexArray#) do not
- have has_side_effects. (They might be marked can_fail, however, because
- you might index out of bounds.)
-
- Using has_side_effects in this way is a bit of a blunt instrument. We could
- be more refined by splitting read and write effects (see comments with #3207
- and #20195)
-
----------- can_fail ----------------------------
-A primop "can_fail" if it can fail with an *unchecked* exception on
-some elements of its input domain. Main examples:
- division (fails on zero denominator)
- array indexing (fails if the index is out of bounds)
-
-An "unchecked exception" is one that is an outright error, (not
-turned into a Haskell exception,) such as seg-fault or
-divide-by-zero error. Such can_fail primops are ALWAYS surrounded
-with a test that checks for the bad cases, but we need to be
-very careful about code motion that might move it out of
-the scope of the test.
-
-Note [Transformations affected by can_fail and has_side_effects]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-The can_fail and has_side_effects properties have the following effect
-on program transformations. Summary table is followed by details.
-
- can_fail has_side_effects
-Discard YES NO
-Float in YES YES
-Float out NO NO
-Duplicate YES NO
-
-* Discarding. case (a `op` b) of _ -> rhs ===> rhs
- You should not discard a has_side_effects primop; e.g.
- case (writeIntArray# a i v s of (# _, _ #) -> True
- Arguably you should be able to discard this, since the
- returned stat token is not used, but that relies on NEVER
- inlining unsafePerformIO, and programmers sometimes write
- this kind of stuff by hand (#9390). So we (conservatively)
- never discard a has_side_effects primop.
-
- However, it's fine to discard a can_fail primop. For example
- case (indexIntArray# a i) of _ -> True
- We can discard indexIntArray#; it has can_fail, but not
- has_side_effects; see #5658 which was all about this.
- Notice that indexIntArray# is (in a more general handling of
- effects) read effect, but we don't care about that here, and
- treat read effects as *not* has_side_effects.
-
- Similarly (a `/#` b) can be discarded. It can seg-fault or
- cause a hardware exception, but not a synchronous Haskell
- exception.
-
-
-
- Synchronous Haskell exceptions, e.g. from raiseIO#, are treated
- as has_side_effects and hence are not discarded.
-
-* Float in. You can float a can_fail or has_side_effects primop
- *inwards*, but not inside a lambda (see Duplication below).
-
-* Float out. You must not float a can_fail primop *outwards* lest
- you escape the dynamic scope of the test. Example:
+
+Note [Exceptions: asynchronous, synchronous, and unchecked]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+There are three very different sorts of things in GHC-Haskell that are
+sometimes called exceptions:
+
+* Haskell exceptions:
+
+ These are ordinary exceptions that users can raise with the likes
+ of 'throw' and handle with the likes of 'catch'. They come in two
+ very different flavors:
+
+ * Asynchronous exceptions:
+ * These can arise at nearly any time, and may have nothing to do
+ with the code being executed.
+ * The compiler itself mostly doesn't need to care about them.
+ * Examples: a signal from another process, running out of heap or stack
+ * Even pure code can receive asynchronous exceptions; in this
+ case, executing the same code again may lead to different
+ results, because the exception may not happen next time.
+ * See rts/RaiseAsync.c for the gory details of how they work.
+
+ * Synchronous exceptions:
+ * These are produced by the code being executed, most commonly via
+ a call to the `raise#` or `raiseIO#` primops.
+ * At run-time, if a piece of pure code raises a synchronous
+ exception, it will always raise the same synchronous exception
+ if it is run again (and not interrupted by an asynchronous
+ exception).
+ * In particular, if an updatable thunk does some work and then
+ raises a synchronous exception, it is safe to overwrite it with
+ a thunk that /immediately/ raises the same exception.
+ * Although we are careful not to discard synchronous exceptions,
+ we are very liberal about re-ordering them with respect to other
+ operations. See the paper "A semantics for imprecise exceptions"
+ as well as Note [Precise exceptions and strictness analysis] in
+ GHC.Types.Demand.
+
+* Unchecked exceptions:
+
+ * These are nasty failures like seg-faults or primitive Int# division
+ by zero. They differ from Haskell exceptions in that they are
+ un-recoverable and typically bring execution to an immediate halt.
+ * We generally treat unchecked exceptions as undefined behavior, on
+ the assumption that the programmer never intends to crash the
+ program in this way. Thus we have no qualms about replacing a
+ division-by-zero with a recoverable Haskell exception or
+ discarding an indexArray# operation whose result is unused.
+
+
+Note [Classifying primop effects]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+Each primop has an associated 'PrimOpEffect', based on what that
+primop can or cannot do at runtime. This classification is
+
+* Recorded in the 'effect' field in primops.txt.pp, and
+* Exposed to the compiler via the 'primOpEffect' function in this module.
+
+See Note [Transformations affected by primop effects] for how we make
+use of this categorisation.
+
+The meanings of the four constructors of 'PrimOpEffect' are as
+follows, in decreasing order of permissiveness:
+
+* ReadWriteEffect
+ A primop is marked ReadWriteEffect if it can
+ - read or write to the world (I/O), or
+ - read or write to a mutable data structure (e.g. readMutVar#).
+
+ Every such primop uses State# tokens for sequencing, with a type like:
+ Inputs -> State# s -> (# State# s, Outputs #)
+ The state token threading expresses ordering, but duplicating even
+ a read-only effect would defeat this. (See "duplication" under
+ Note [Transformations affected by primop effects] for details.)
+
+ Note that operations like `indexArray#` that read *immutable*
+ data structures do not need such special sequencing-related care,
+ and are therefore not marked ReadWriteEffect.
+
+* ThrowsException
+ A primop is marked ThrowsException if
+ - it is not marked ReadWriteEffect, and
+ - it may diverge or throw a synchronous Haskell exception
+ even when used in a "correct" and well-specified way.
+
+ See also Note [Exceptions: asynchronous, synchronous, and unchecked].
+ Examples include raise#, raiseIO#, dataToTag#, and seq#.
+
+ Note that whether an exception is considered precise or imprecise
+ does not matter for the purposes of the PrimOpEffect flag.
+
+* CanFail
+ A primop is marked CanFail if
+ - it is not marked ReadWriteEffect or ThrowsException, and
+ - it can trigger a (potentially-unchecked) exception when used incorrectly.
+
+ See Note [Exceptions: asynchronous, synchronous, and unchecked].
+ Examples include quotWord# and indexIntArray#, which can fail with
+ division-by-zero and a segfault respectively.
+
+ A correct use of a CanFail primop is usually surrounded by a test
+ that screens out the bad cases such as a zero divisor or an
+ out-of-bounds array index. We must take care never to move a
+ CanFail primop outside the scope of such a test.
+
+* NoEffect
+ A primop is marked NoEffect if it does not belong to any of the
+ other three categories. We can very aggressively shuffle these
+ operations around without fear of changing a program's meaning.
+
+ Perhaps surprisingly, this aggressive shuffling imposes another
+ restriction: The tricky NoEffect primop uncheckedShiftLWord32# has
+ an undefined result when the provided shift amount is not between
+ 0 and 31. Thus, a call like `uncheckedShiftLWord32# x 95#` is
+ obviously invalid. But since uncheckedShiftLWord32# is marked
+ NoEffect, we may float such an invalid call out of a dead branch
+ and speculatively evaluate it.
+
+ In particular, we cannot safely rewrite such an invalid call to a
+ runtime error; we must emit code that produces a valid Word32#.
+ (If we're lucky, Core Lint may complain that the result of such a
+ rewrite violates the let-can-float invariant (#16742), but the
+ rewrite is always wrong!) See also Note [Guarding against silly shifts]
+ in GHC.Core.Opt.ConstantFold.
+
+ Marking uncheckedShiftLWord32# as CanFail instead of NoEffect
+ would give us the freedom to rewrite such invalid calls to runtime
+ errors, but would get in the way of optimization: When speculatively
+ executing a bit-shift prevents the allocation of a thunk, that's a
+ big win.
+
+
+Note [Transformations affected by primop effects]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+The PrimOpEffect properties have the following effect on program
+transformations. The summary table is followed by details. See also
+Note [Classifying primop effects] for exactly what each column means.
+
+ NoEffect CanFail ThrowsException ReadWriteEffect
+Discard YES YES NO NO
+Defer (float in) YES YES SAFE SAFE
+Speculate (float out) YES NO NO NO
+Duplicate YES YES YES NO
+
+(SAFE means we could perform the transformation but do not.)
+
+* Discarding: case (a `op` b) of _ -> rhs ===> rhs
+ You should not discard a ReadWriteEffect primop; e.g.
+ case (writeIntArray# a i v s of (# _, _ #) -> True
+ One could argue in favor of discarding this, since the returned
+ State# token is not used. But in practice unsafePerformIO can
+ easily produce similar code, and programmers sometimes write this
+ kind of stuff by hand (#9390). So we (conservatively) never discard
+ a ReadWriteEffect primop.
+
+ Digression: We could try to track read-only effects separately
+ from write effects to allow the former to be discarded. But in
+ fact we want a more general rewrite for read-only operations:
+ case readOp# state# of (# newState#, _unused_result #) -> body
+ ==> case state# of newState# -> body
+ Such a rewrite is not yet implemented, but would have to be done
+ in a different place anyway.
+
+ Discarding a ThrowsException primop would also discard any exception
+ it might have thrown. For `raise#` or `raiseIO#` this would defeat
+ the whole point of the primop, while for `dataToTag#` or `seq#` this
+ would make programs unexpectly lazier.
+
+ However, it's fine to discard a CanFail primop. For example
+ case (indexIntArray# a i) of _ -> True
+ We can discard indexIntArray# here; this came up in #5658. Notice
+ that CanFail primops like indexIntArray# can only trigger an
+ exception when used incorrectly, i.e. a call that might not succeed
+ is undefined behavior anyway.
+
+* Deferring (float-in):
+ See Note [Floating primops] in GHC.Core.Opt.FloatIn.
+
+ In the absence of data dependencies (including state token threading),
+ we reserve the right to re-order the following things arbitrarily:
+ * Side effects
+ * Imprecise exceptions
+ * Divergent computations (infinite loops)
+ This lets us safely float almost any primop *inwards*, but not
+ inside a (multi-shot) lambda. (See "Duplication" below.)
+
+ However, the main reason to float-in a primop application would be
+ to discard it (by floating it into some but not all branches of a
+ case), so we actually only float-in NoEffect and CanFail operations.
+ See also Note [Floating primops] in GHC.Core.Opt.FloatIn.
+
+ (This automatically side-steps the question of precise exceptions, which
+ mustn't be re-ordered arbitrarily but need at least ThrowsException.)
+
+* Speculation (strict float-out):
+ You must not float a CanFail primop *outwards* lest it escape the
+ dynamic scope of a run-time validity test. Example:
case d ># 0# of
True -> case x /# d of r -> r +# 1
False -> 0
- Here we must not float the case outwards to give
+ Here we must not float the case outwards to give
case x/# d of r ->
case d ># 0# of
True -> r +# 1
False -> 0
+ Otherwise, if this block is reached when d is zero, it will crash.
+ Exactly the same reasoning applies to ThrowsException primops.
- Nor can you float out a has_side_effects primop. For example:
+ Nor can you float out a ReadWriteEffect primop. For example:
if blah then case writeMutVar# v True s0 of (# s1 #) -> s1
else s0
- Notice that s0 is mentioned in both branches of the 'if', but
- only one of these two will actually be consumed. But if we
- float out to
+ Notice that s0 is mentioned in both branches of the 'if', but
+ only one of these two will actually be consumed. But if we
+ float out to
case writeMutVar# v True s0 of (# s1 #) ->
if blah then s1 else s0
- the writeMutVar will be performed in both branches, which is
- utterly wrong.
-
-* Duplication. You cannot duplicate a has_side_effect primop. You
- might wonder how this can occur given the state token threading, but
- just look at Control.Monad.ST.Lazy.Imp.strictToLazy! We get
- something like this
+ the writeMutVar will be performed in both branches, which is
+ utterly wrong.
+
+ What about a read-only operation that cannot fail, like
+ readMutVar#? In principle we could safely float these out. But
+ there are not very many such operations and it's not clear if
+ there are real-world programs that would benefit from this.
+
+* Duplication:
+ You cannot duplicate a ReadWriteEffect primop. You might wonder
+ how this can occur given the state token threading, but just look
+ at Control.Monad.ST.Lazy.Imp.strictToLazy! We get something like this
p = case readMutVar# s v of
(# s', r #) -> (State# s', r)
s' = case p of (s', r) -> s'
r = case p of (s', r) -> r
- (All these bindings are boxed.) If we inline p at its two call
- sites, we get a catastrophe: because the read is performed once when
- s' is demanded, and once when 'r' is demanded, which may be much
- later. Utterly wrong. #3207 is real example of this happening.
+ (All these bindings are boxed.) If we inline p at its two call
+ sites, we get a catastrophe: because the read is performed once when
+ s' is demanded, and once when 'r' is demanded, which may be much
+ later. Utterly wrong. #3207 is real example of this happening.
+ Floating p into a multi-shot lambda would be wrong for the same reason.
+
+ However, it's fine to duplicate a CanFail or ThrowsException primop.
- However, it's fine to duplicate a can_fail primop. That is really
- the only difference between can_fail and has_side_effects.
-Note [Implementation: how can_fail/has_side_effects affect transformations]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+Note [Implementation: how PrimOpEffect affects transformations]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
How do we ensure that floating/duplication/discarding are done right
in the simplifier?
-Two main predicates on primops test these flags:
- primOpOkForSideEffects <=> not has_side_effects
- primOpOkForSpeculation <=> not (has_side_effects || can_fail)
+Several predicates on primops test this flag:
+ primOpOkToDiscard <=> effect < ThrowsException
+ primOpOkForSpeculation <=> effect == NoEffect && not (out_of_line)
+ primOpIsCheap <=> cheap -- ...defaults to primOpOkForSpeculation
+ [[But note that the raise# family and seq# are also considered cheap in
+ GHC.Core.Utils.exprIsCheap by way of being work-free]]
+
+ * The discarding mentioned above happens in
+ GHC.Core.Opt.Simplify.Iteration, specifically in rebuildCase,
+ where it is guarded by exprOkToDiscard, which in turn checks
+ primOpOkToDiscard.
* The "no-float-out" thing is achieved by ensuring that we never
- let-bind a can_fail or has_side_effects primop. The RHS of a
- let-binding (which can float in and out freely) satisfies
- exprOkForSpeculation; this is the let-can-float invariant. And
- exprOkForSpeculation is false of can_fail and has_side_effects.
+ let-bind a saturated primop application unless it has NoEffect.
+ The RHS of a let-binding (which can float in and out freely)
+ satisfies exprOkForSpeculation; this is the let-can-float
+ invariant. And exprOkForSpeculation is false of a saturated
+ primop application unless it has NoEffect.
- * So can_fail and has_side_effects primops will appear only as the
+ * So primops that aren't NoEffect will appear only as the
scrutinees of cases, and that's why the FloatIn pass is capable
of floating case bindings inwards.
- * The no-duplicate thing is done via primOpIsCheap, by making
- has_side_effects things (very very very) not-cheap!
+ * Duplication via inlining and float-in of (lifted) let-binders is
+ controlled via primOpIsWorkFree and primOpIsCheap, by making
+ ReadWriteEffect things (among others) not-cheap! (The test
+ PrimOpEffect_Sanity will complain if any ReadWriteEffect primop
+ is considered either work-free or cheap.) Additionally, a
+ case binding is only floated inwards if its scrutinee is ok-to-discard.
-}
-primOpHasSideEffects :: PrimOp -> Bool
-#include "primop-has-side-effects.hs-incl"
+primOpEffect :: PrimOp -> PrimOpEffect
+#include "primop-effects.hs-incl"
-primOpCanFail :: PrimOp -> Bool
-#include "primop-can-fail.hs-incl"
+data PrimOpEffect
+ -- See Note [Classifying primop effects]
+ = NoEffect
+ | CanFail
+ | ThrowsException
+ | ReadWriteEffect
+ deriving (Eq, Ord)
primOpOkForSpeculation :: PrimOp -> Bool
- -- See Note [PrimOp can_fail and has_side_effects]
+ -- See Note [Classifying primop effects]
-- See comments with GHC.Core.Utils.exprOkForSpeculation
- -- primOpOkForSpeculation => primOpOkForSideEffects
+ -- primOpOkForSpeculation => primOpOkToDiscard
primOpOkForSpeculation op
- = primOpOkForSideEffects op
- && not (primOpOutOfLine op || primOpCanFail op)
+ = primOpEffect op == NoEffect && not (primOpOutOfLine op)
-- I think the "out of line" test is because out of line things can
-- be expensive (eg sine, cosine), and so we may not want to speculate them
-primOpOkForSideEffects :: PrimOp -> Bool
-primOpOkForSideEffects op
- = not (primOpHasSideEffects op)
+primOpOkToDiscard :: PrimOp -> Bool
+primOpOkToDiscard op
+ = primOpEffect op < ThrowsException
-{-
-Note [primOpIsCheap]
-~~~~~~~~~~~~~~~~~~~~
-
- at primOpIsCheap@, as used in GHC.Core.Opt.Simplify.Utils. For now (HACK
-WARNING), we just borrow some other predicates for a
-what-should-be-good-enough test. "Cheap" means willing to call it more
-than once, and/or push it inside a lambda. The latter could change the
-behaviour of 'seq' for primops that can fail, so we don't treat them as cheap.
--}
+primOpIsWorkFree :: PrimOp -> Bool
+#include "primop-is-work-free.hs-incl"
primOpIsCheap :: PrimOp -> Bool
--- See Note [PrimOp can_fail and has_side_effects]
-primOpIsCheap op = primOpOkForSpeculation op
+-- See Note [Classifying primop effects]
+#include "primop-is-cheap.hs-incl"
-- In March 2001, we changed this to
-- primOpIsCheap op = False
-- thereby making *no* primops seem cheap. But this killed eta
@@ -540,7 +637,7 @@ primOpIsCheap op = primOpOkForSpeculation op
-- The problem that originally gave rise to the change was
-- let x = a +# b *# c in x +# x
-- were we don't want to inline x. But primopIsCheap doesn't control
--- that (it's exprIsDupable that does) so the problem doesn't occur
+-- that (it's primOpIsWorkFree that does) so the problem doesn't occur
-- even if primOpIsCheap sometimes says 'True'.
=====================================
compiler/GHC/Builtin/primops.txt.pp
=====================================
@@ -136,11 +136,13 @@
-- Int64X2#, SCALAR expands to Int64#, and VECTUPLE expands to (# Int64#, Int64# #).
defaults
- has_side_effects = False
+ effect = NoEffect -- See Note [Classifying primop effects] in GHC.Builtin.PrimOps
+ can_fail_warning = WarnIfEffectIsCanFail
out_of_line = False -- See Note [When do out-of-line primops go in primops.txt.pp]
- can_fail = False -- See Note [PrimOp can_fail and has_side_effects] in PrimOp
commutable = False
code_size = { primOpCodeSizeDefault }
+ work_free = { primOpCodeSize _thisOp == 0 }
+ cheap = { primOpOkForSpeculation _thisOp }
strictness = { \ arity -> mkClosedDmdSig (replicate arity topDmd) topDiv }
fixity = Nothing
llvm_only = False
@@ -166,8 +168,7 @@ defaults
--
-- - No polymorphism in type
-- - `strictness = <default>`
--- - `can_fail = False`
--- - `has_side_effects = True`
+-- - `effect = ReadWriteEffect`
--
-- https://gitlab.haskell.org/ghc/ghc/issues/16929 tracks this issue,
-- and has a table of which external-only primops are blocked by which
@@ -295,15 +296,15 @@ primop Int8MulOp "timesInt8#" GenPrimOp Int8# -> Int8# -> Int8#
primop Int8QuotOp "quotInt8#" GenPrimOp Int8# -> Int8# -> Int8#
with
- can_fail = True
+ effect = CanFail
primop Int8RemOp "remInt8#" GenPrimOp Int8# -> Int8# -> Int8#
with
- can_fail = True
+ effect = CanFail
primop Int8QuotRemOp "quotRemInt8#" GenPrimOp Int8# -> Int8# -> (# Int8#, Int8# #)
with
- can_fail = True
+ effect = CanFail
primop Int8SllOp "uncheckedShiftLInt8#" GenPrimOp Int8# -> Int# -> Int8#
primop Int8SraOp "uncheckedShiftRAInt8#" GenPrimOp Int8# -> Int# -> Int8#
@@ -341,15 +342,15 @@ primop Word8MulOp "timesWord8#" GenPrimOp Word8# -> Word8# -> Word8#
primop Word8QuotOp "quotWord8#" GenPrimOp Word8# -> Word8# -> Word8#
with
- can_fail = True
+ effect = CanFail
primop Word8RemOp "remWord8#" GenPrimOp Word8# -> Word8# -> Word8#
with
- can_fail = True
+ effect = CanFail
primop Word8QuotRemOp "quotRemWord8#" GenPrimOp Word8# -> Word8# -> (# Word8#, Word8# #)
with
- can_fail = True
+ effect = CanFail
primop Word8AndOp "andWord8#" GenPrimOp Word8# -> Word8# -> Word8#
with commutable = True
@@ -399,15 +400,15 @@ primop Int16MulOp "timesInt16#" GenPrimOp Int16# -> Int16# -> Int16#
primop Int16QuotOp "quotInt16#" GenPrimOp Int16# -> Int16# -> Int16#
with
- can_fail = True
+ effect = CanFail
primop Int16RemOp "remInt16#" GenPrimOp Int16# -> Int16# -> Int16#
with
- can_fail = True
+ effect = CanFail
primop Int16QuotRemOp "quotRemInt16#" GenPrimOp Int16# -> Int16# -> (# Int16#, Int16# #)
with
- can_fail = True
+ effect = CanFail
primop Int16SllOp "uncheckedShiftLInt16#" GenPrimOp Int16# -> Int# -> Int16#
primop Int16SraOp "uncheckedShiftRAInt16#" GenPrimOp Int16# -> Int# -> Int16#
@@ -445,15 +446,15 @@ primop Word16MulOp "timesWord16#" GenPrimOp Word16# -> Word16# -> Word16#
primop Word16QuotOp "quotWord16#" GenPrimOp Word16# -> Word16# -> Word16#
with
- can_fail = True
+ effect = CanFail
primop Word16RemOp "remWord16#" GenPrimOp Word16# -> Word16# -> Word16#
with
- can_fail = True
+ effect = CanFail
primop Word16QuotRemOp "quotRemWord16#" GenPrimOp Word16# -> Word16# -> (# Word16#, Word16# #)
with
- can_fail = True
+ effect = CanFail
primop Word16AndOp "andWord16#" GenPrimOp Word16# -> Word16# -> Word16#
with commutable = True
@@ -503,15 +504,15 @@ primop Int32MulOp "timesInt32#" GenPrimOp Int32# -> Int32# -> Int32#
primop Int32QuotOp "quotInt32#" GenPrimOp Int32# -> Int32# -> Int32#
with
- can_fail = True
+ effect = CanFail
primop Int32RemOp "remInt32#" GenPrimOp Int32# -> Int32# -> Int32#
with
- can_fail = True
+ effect = CanFail
primop Int32QuotRemOp "quotRemInt32#" GenPrimOp Int32# -> Int32# -> (# Int32#, Int32# #)
with
- can_fail = True
+ effect = CanFail
primop Int32SllOp "uncheckedShiftLInt32#" GenPrimOp Int32# -> Int# -> Int32#
primop Int32SraOp "uncheckedShiftRAInt32#" GenPrimOp Int32# -> Int# -> Int32#
@@ -549,15 +550,15 @@ primop Word32MulOp "timesWord32#" GenPrimOp Word32# -> Word32# -> Word32#
primop Word32QuotOp "quotWord32#" GenPrimOp Word32# -> Word32# -> Word32#
with
- can_fail = True
+ effect = CanFail
primop Word32RemOp "remWord32#" GenPrimOp Word32# -> Word32# -> Word32#
with
- can_fail = True
+ effect = CanFail
primop Word32QuotRemOp "quotRemWord32#" GenPrimOp Word32# -> Word32# -> (# Word32#, Word32# #)
with
- can_fail = True
+ effect = CanFail
primop Word32AndOp "andWord32#" GenPrimOp Word32# -> Word32# -> Word32#
with commutable = True
@@ -607,11 +608,11 @@ primop Int64MulOp "timesInt64#" GenPrimOp Int64# -> Int64# -> Int64#
primop Int64QuotOp "quotInt64#" GenPrimOp Int64# -> Int64# -> Int64#
with
- can_fail = True
+ effect = CanFail
primop Int64RemOp "remInt64#" GenPrimOp Int64# -> Int64# -> Int64#
with
- can_fail = True
+ effect = CanFail
primop Int64SllOp "uncheckedIShiftL64#" GenPrimOp Int64# -> Int# -> Int64#
primop Int64SraOp "uncheckedIShiftRA64#" GenPrimOp Int64# -> Int# -> Int64#
@@ -649,11 +650,11 @@ primop Word64MulOp "timesWord64#" GenPrimOp Word64# -> Word64# -> Word64#
primop Word64QuotOp "quotWord64#" GenPrimOp Word64# -> Word64# -> Word64#
with
- can_fail = True
+ effect = CanFail
primop Word64RemOp "remWord64#" GenPrimOp Word64# -> Word64# -> Word64#
with
- can_fail = True
+ effect = CanFail
primop Word64AndOp "and64#" GenPrimOp Word64# -> Word64# -> Word64#
with commutable = True
@@ -736,19 +737,19 @@ primop IntQuotOp "quotInt#" GenPrimOp
{Rounds towards zero. The behavior is undefined if the second argument is
zero.
}
- with can_fail = True
+ with effect = CanFail
primop IntRemOp "remInt#" GenPrimOp
Int# -> Int# -> Int#
{Satisfies @('quotInt#' x y) '*#' y '+#' ('remInt#' x y) == x at . The
behavior is undefined if the second argument is zero.
}
- with can_fail = True
+ with effect = CanFail
primop IntQuotRemOp "quotRemInt#" GenPrimOp
Int# -> Int# -> (# Int#, Int# #)
{Rounds towards zero.}
- with can_fail = True
+ with effect = CanFail
primop IntAndOp "andI#" GenPrimOp Int# -> Int# -> Int#
{Bitwise "and".}
@@ -885,20 +886,20 @@ primop WordMul2Op "timesWord2#" GenPrimOp
with commutable = True
primop WordQuotOp "quotWord#" GenPrimOp Word# -> Word# -> Word#
- with can_fail = True
+ with effect = CanFail
primop WordRemOp "remWord#" GenPrimOp Word# -> Word# -> Word#
- with can_fail = True
+ with effect = CanFail
primop WordQuotRemOp "quotRemWord#" GenPrimOp
Word# -> Word# -> (# Word#, Word# #)
- with can_fail = True
+ with effect = CanFail
primop WordQuotRem2Op "quotRemWord2#" GenPrimOp
Word# -> Word# -> Word# -> (# Word#, Word# #)
{ Takes high word of dividend, then low word of dividend, then divisor.
Requires that high word < divisor.}
- with can_fail = True
+ with effect = CanFail
primop WordAndOp "and#" GenPrimOp Word# -> Word# -> Word#
with commutable = True
@@ -1108,7 +1109,7 @@ primop DoubleMulOp "*##" GenPrimOp
primop DoubleDivOp "/##" GenPrimOp
Double# -> Double# -> Double#
- with can_fail = True
+ with effect = CanFail -- Can this one really fail?
fixity = infixl 7
primop DoubleNegOp "negateDouble#" GenPrimOp Double# -> Double#
@@ -1136,13 +1137,13 @@ primop DoubleLogOp "logDouble#" GenPrimOp
Double# -> Double#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop DoubleLog1POp "log1pDouble#" GenPrimOp
Double# -> Double#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop DoubleSqrtOp "sqrtDouble#" GenPrimOp
Double# -> Double#
@@ -1168,13 +1169,13 @@ primop DoubleAsinOp "asinDouble#" GenPrimOp
Double# -> Double#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop DoubleAcosOp "acosDouble#" GenPrimOp
Double# -> Double#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop DoubleAtanOp "atanDouble#" GenPrimOp
Double# -> Double#
@@ -1263,7 +1264,7 @@ primop FloatMulOp "timesFloat#" GenPrimOp
primop FloatDivOp "divideFloat#" GenPrimOp
Float# -> Float# -> Float#
- with can_fail = True
+ with effect = CanFail
primop FloatNegOp "negateFloat#" GenPrimOp Float# -> Float#
@@ -1288,13 +1289,13 @@ primop FloatLogOp "logFloat#" GenPrimOp
Float# -> Float#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop FloatLog1POp "log1pFloat#" GenPrimOp
Float# -> Float#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop FloatSqrtOp "sqrtFloat#" GenPrimOp
Float# -> Float#
@@ -1320,13 +1321,13 @@ primop FloatAsinOp "asinFloat#" GenPrimOp
Float# -> Float#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop FloatAcosOp "acosFloat#" GenPrimOp
Float# -> Float#
with
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
+ effect = CanFail
primop FloatAtanOp "atanFloat#" GenPrimOp
Float# -> Float#
@@ -1461,22 +1462,22 @@ primop NewArrayOp "newArray#" GenPrimOp
with each element containing the specified initial value.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ReadArrayOp "readArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> State# s -> (# State# s, a_levpoly #)
{Read from specified index of mutable array. Result is not yet evaluated.}
with
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop WriteArrayOp "writeArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> a_levpoly -> State# s -> State# s
{Write to specified index of mutable array.}
with
- has_side_effects = True
- can_fail = True
- code_size = 2 -- card update too
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
+ code_size = 2 -- card update too
primop SizeofArrayOp "sizeofArray#" GenPrimOp
Array# a_levpoly -> Int#
@@ -1496,20 +1497,20 @@ primop IndexArrayOp "indexArray#" GenPrimOp
heap. Avoiding these thunks, in turn, reduces references to the
argument array, allowing it to be garbage collected more promptly.}
with
- can_fail = True
+ effect = CanFail
primop UnsafeFreezeArrayOp "unsafeFreezeArray#" GenPrimOp
MutableArray# s a_levpoly -> State# s -> (# State# s, Array# a_levpoly #)
{Make a mutable array immutable, without copying.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
primop UnsafeThawArrayOp "unsafeThawArray#" GenPrimOp
Array# a_levpoly -> State# s -> (# State# s, MutableArray# s a_levpoly #)
{Make an immutable array mutable, without copying.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop CopyArrayOp "copyArray#" GenPrimOp
Array# a_levpoly -> Int# -> MutableArray# s a_levpoly -> Int# -> Int# -> State# s -> State# s
@@ -1522,8 +1523,8 @@ primop CopyArrayOp "copyArray#" GenPrimOp
either.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CopyMutableArrayOp "copyMutableArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> MutableArray# s a_levpoly -> Int# -> Int# -> State# s -> State# s
@@ -1536,8 +1537,8 @@ primop CopyMutableArrayOp "copyMutableArray#" GenPrimOp
destination regions may overlap.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CloneArrayOp "cloneArray#" GenPrimOp
Array# a_levpoly -> Int# -> Int# -> Array# a_levpoly
@@ -1547,8 +1548,8 @@ primop CloneArrayOp "cloneArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect -- assumed too expensive to duplicate?
+ can_fail_warning = YesWarnCanFail
primop CloneMutableArrayOp "cloneMutableArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> Int# -> State# s -> (# State# s, MutableArray# s a_levpoly #)
@@ -1558,8 +1559,8 @@ primop CloneMutableArrayOp "cloneMutableArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FreezeArrayOp "freezeArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> Int# -> State# s -> (# State# s, Array# a_levpoly #)
@@ -1569,8 +1570,8 @@ primop FreezeArrayOp "freezeArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop ThawArrayOp "thawArray#" GenPrimOp
Array# a_levpoly -> Int# -> Int# -> State# s -> (# State# s, MutableArray# s a_levpoly #)
@@ -1580,8 +1581,8 @@ primop ThawArrayOp "thawArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasArrayOp "casArray#" GenPrimOp
MutableArray# s a_levpoly -> Int# -> a_levpoly -> a_levpoly -> State# s -> (# State# s, Int#, a_levpoly #)
@@ -1599,8 +1600,8 @@ primop CasArrayOp "casArray#" GenPrimOp
}
with
out_of_line = True
- has_side_effects = True
- can_fail = True -- Might index out of bounds
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
------------------------------------------------------------------------
@@ -1637,7 +1638,7 @@ primop NewSmallArrayOp "newSmallArray#" GenPrimOp
with each element containing the specified initial value.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ShrinkSmallMutableArrayOp_Char "shrinkSmallMutableArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> State# s -> State# s
@@ -1654,21 +1655,23 @@ primop ShrinkSmallMutableArrayOp_Char "shrinkSmallMutableArray#" GenPrimOp
@since 0.6.1}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
+ -- can fail because of the "newSize <= oldSize" requirement
primop ReadSmallArrayOp "readSmallArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> State# s -> (# State# s, a_levpoly #)
{Read from specified index of mutable array. Result is not yet evaluated.}
with
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop WriteSmallArrayOp "writeSmallArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> a_levpoly -> State# s -> State# s
{Write to specified index of mutable array.}
with
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop SizeofSmallArrayOp "sizeofSmallArray#" GenPrimOp
SmallArray# a_levpoly -> Int#
@@ -1693,20 +1696,20 @@ primop IndexSmallArrayOp "indexSmallArray#" GenPrimOp
{Read from specified index of immutable array. Result is packaged into
an unboxed singleton; the result itself is not yet evaluated.}
with
- can_fail = True
+ effect = CanFail
primop UnsafeFreezeSmallArrayOp "unsafeFreezeSmallArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> State# s -> (# State# s, SmallArray# a_levpoly #)
{Make a mutable array immutable, without copying.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
primop UnsafeThawSmallArrayOp "unsafeThawSmallArray#" GenPrimOp
SmallArray# a_levpoly -> State# s -> (# State# s, SmallMutableArray# s a_levpoly #)
{Make an immutable array mutable, without copying.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
-- The code_size is only correct for the case when the copy family of
-- primops aren't inlined. It would be nice to keep track of both.
@@ -1721,9 +1724,9 @@ primop CopySmallArrayOp "copySmallArray#" GenPrimOp
be the same array in different states, but this is not checked
either.}
with
- out_of_line = True
- has_side_effects = True
- can_fail = True
+ out_of_line = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CopySmallMutableArrayOp "copySmallMutableArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> SmallMutableArray# s a_levpoly -> Int# -> Int# -> State# s -> State# s
@@ -1736,9 +1739,9 @@ primop CopySmallMutableArrayOp "copySmallMutableArray#" GenPrimOp
The regions are allowed to overlap, although this is only possible when the same
array is provided as both the source and the destination. }
with
- out_of_line = True
- has_side_effects = True
- can_fail = True
+ out_of_line = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CloneSmallArrayOp "cloneSmallArray#" GenPrimOp
SmallArray# a_levpoly -> Int# -> Int# -> SmallArray# a_levpoly
@@ -1748,8 +1751,8 @@ primop CloneSmallArrayOp "cloneSmallArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect -- assumed too expensive to duplicate?
+ can_fail_warning = YesWarnCanFail
primop CloneSmallMutableArrayOp "cloneSmallMutableArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> Int# -> State# s -> (# State# s, SmallMutableArray# s a_levpoly #)
@@ -1759,8 +1762,8 @@ primop CloneSmallMutableArrayOp "cloneSmallMutableArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FreezeSmallArrayOp "freezeSmallArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> Int# -> State# s -> (# State# s, SmallArray# a_levpoly #)
@@ -1770,8 +1773,8 @@ primop FreezeSmallArrayOp "freezeSmallArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop ThawSmallArrayOp "thawSmallArray#" GenPrimOp
SmallArray# a_levpoly -> Int# -> Int# -> State# s -> (# State# s, SmallMutableArray# s a_levpoly #)
@@ -1781,8 +1784,8 @@ primop ThawSmallArrayOp "thawSmallArray#" GenPrimOp
range, but this is not checked.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasSmallArrayOp "casSmallArray#" GenPrimOp
SmallMutableArray# s a_levpoly -> Int# -> a_levpoly -> a_levpoly -> State# s -> (# State# s, Int#, a_levpoly #)
@@ -1790,8 +1793,8 @@ primop CasSmallArrayOp "casSmallArray#" GenPrimOp
See the documentation of 'casArray#'.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True -- Might index out of bounds
+ effect = ReadWriteEffect -- Might index out of bounds
+ can_fail_warning = YesWarnCanFail
------------------------------------------------------------------------
section "Byte Arrays"
@@ -1857,20 +1860,23 @@ primop NewByteArrayOp_Char "newByteArray#" GenPrimOp
the specified state thread. The size of the memory underlying the
array will be rounded up to the platform's word size.}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop NewPinnedByteArrayOp_Char "newPinnedByteArray#" GenPrimOp
Int# -> State# s -> (# State# s, MutableByteArray# s #)
{Like 'newByteArray#' but GC guarantees not to move it.}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop NewAlignedPinnedByteArrayOp_Char "newAlignedPinnedByteArray#" GenPrimOp
Int# -> Int# -> State# s -> (# State# s, MutableByteArray# s #)
{Like 'newPinnedByteArray#' but allow specifying an arbitrary
alignment, which must be a power of two.}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
+ -- can fail warning for the "power of two" requirement
+ -- TODO: Fact-check this.
primop MutableByteArrayIsPinnedOp "isMutableByteArrayPinned#" GenPrimOp
MutableByteArray# s -> Int#
@@ -1903,7 +1909,9 @@ primop ShrinkMutableByteArrayOp_Char "shrinkMutableByteArray#" GenPrimOp
@since 0.4.0.0}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
+ -- can fail for the "newSize <= oldSize" requirement
primop ResizeMutableByteArrayOp_Char "resizeMutableByteArray#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> (# State# s,MutableByteArray# s #)
@@ -1921,13 +1929,14 @@ primop ResizeMutableByteArrayOp_Char "resizeMutableByteArray#" GenPrimOp
@since 0.4.0.0}
with out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop UnsafeFreezeByteArrayOp "unsafeFreezeByteArray#" GenPrimOp
MutableByteArray# s -> State# s -> (# State# s, ByteArray# #)
{Make a mutable byte array immutable, without copying.}
with
- has_side_effects = True
+ -- why was this has_side_effects?
+ code_size = 0
primop UnsafeThawByteArrayOp "unsafeThawByteArray#" GenPrimOp
ByteArray# -> State# s -> (# State# s, MutableByteArray# s #)
@@ -1935,7 +1944,8 @@ primop UnsafeThawByteArrayOp "unsafeThawByteArray#" GenPrimOp
@since 0.12.0.0}
with
- has_side_effects = True
+ -- why was this has_side_effects?
+ code_size = 0
primop SizeofByteArrayOp "sizeofByteArray#" GenPrimOp
ByteArray# -> Int#
@@ -1976,7 +1986,7 @@ primop CompareByteArraysOp "compareByteArrays#" GenPrimOp
@since 0.5.2.0}
with
- can_fail = True
+ effect = CanFail
primop CopyByteArrayOp "copyByteArray#" GenPrimOp
ByteArray# -> Int# -> MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
@@ -1989,9 +1999,9 @@ primop CopyByteArrayOp "copyByteArray#" GenPrimOp
either.
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4}
- can_fail = True
primop CopyMutableByteArrayOp "copyMutableByteArray#" GenPrimOp
MutableByteArray# s -> Int# -> MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
@@ -2004,9 +2014,9 @@ primop CopyMutableByteArrayOp "copyMutableByteArray#" GenPrimOp
array is provided as both the source and the destination.
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop CopyMutableByteArrayNonOverlappingOp "copyMutableByteArrayNonOverlapping#" GenPrimOp
MutableByteArray# s -> Int# -> MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
@@ -2020,9 +2030,9 @@ primop CopyMutableByteArrayNonOverlappingOp "copyMutableByteArrayNonOverlapping
@since 0.11.0
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop CopyByteArrayToAddrOp "copyByteArrayToAddr#" GenPrimOp
ByteArray# -> Int# -> Addr# -> Int# -> State# s -> State# s
@@ -2032,9 +2042,9 @@ primop CopyByteArrayToAddrOp "copyByteArrayToAddr#" GenPrimOp
ByteArray\# (e.g. if the ByteArray\# were pinned), but this is not checked
either.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop CopyMutableByteArrayToAddrOp "copyMutableByteArrayToAddr#" GenPrimOp
MutableByteArray# s -> Int# -> Addr# -> Int# -> State# s -> State# s
@@ -2044,9 +2054,9 @@ primop CopyMutableByteArrayToAddrOp "copyMutableByteArrayToAddr#" GenPrimOp
point into the MutableByteArray\# (e.g. if the MutableByteArray\# were
pinned), but this is not checked either.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop CopyAddrToByteArrayOp "copyAddrToByteArray#" GenPrimOp
Addr# -> MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
@@ -2056,9 +2066,9 @@ primop CopyAddrToByteArrayOp "copyAddrToByteArray#" GenPrimOp
point into the MutableByteArray\# (e.g. if the MutableByteArray\# were pinned),
but this is not checked either.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop CopyAddrToAddrOp "copyAddrToAddr#" GenPrimOp
Addr# -> Addr# -> Int# -> State# RealWorld -> State# RealWorld
@@ -2071,9 +2081,9 @@ primop CopyAddrToAddrOp "copyAddrToAddr#" GenPrimOp
@since 0.11.0
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
primop CopyAddrToAddrNonOverlappingOp "copyAddrToAddrNonOverlapping#" GenPrimOp
Addr# -> Addr# -> Int# -> State# RealWorld -> State# RealWorld
@@ -2087,18 +2097,18 @@ primop CopyAddrToAddrNonOverlappingOp "copyAddrToAddrNonOverlapping#" GenPrimOp
@since 0.11.0
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
primop SetByteArrayOp "setByteArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> Int# -> State# s -> State# s
{@'setByteArray#' ba off len c@ sets the byte range @[off, off+len)@ of
the 'MutableByteArray#' to the byte @c at .}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall + 4 }
- can_fail = True
primop SetAddrRangeOp "setAddrRange#" GenPrimOp
Addr# -> Int# -> Int# -> State# RealWorld -> State# RealWorld
@@ -2111,9 +2121,9 @@ primop SetAddrRangeOp "setAddrRange#" GenPrimOp
@since 0.11.0
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
code_size = { primOpCodeSizeForeignCall }
- can_fail = True
-- Atomic operations
@@ -2121,15 +2131,17 @@ primop AtomicReadByteArrayOp_Int "atomicReadIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> (# State# s, Int# #)
{Given an array and an offset in machine words, read an element. The
index is assumed to be in bounds. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop AtomicWriteByteArrayOp_Int "atomicWriteIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> State# s
{Given an array and an offset in machine words, write an element. The
index is assumed to be in bounds. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasByteArrayOp_Int "casIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> Int# -> State# s -> (# State# s, Int# #)
@@ -2138,8 +2150,9 @@ primop CasByteArrayOp_Int "casIntArray#" GenPrimOp
value if the current value matches the provided old value. Returns
the value of the element before the operation. Implies a full memory
barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasByteArrayOp_Int8 "casInt8Array#" GenPrimOp
MutableByteArray# s -> Int# -> Int8# -> Int8# -> State# s -> (# State# s, Int8# #)
@@ -2148,8 +2161,9 @@ primop CasByteArrayOp_Int8 "casInt8Array#" GenPrimOp
value if the current value matches the provided old value. Returns
the value of the element before the operation. Implies a full memory
barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasByteArrayOp_Int16 "casInt16Array#" GenPrimOp
MutableByteArray# s -> Int# -> Int16# -> Int16# -> State# s -> (# State# s, Int16# #)
@@ -2158,8 +2172,9 @@ primop CasByteArrayOp_Int16 "casInt16Array#" GenPrimOp
value if the current value matches the provided old value. Returns
the value of the element before the operation. Implies a full memory
barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasByteArrayOp_Int32 "casInt32Array#" GenPrimOp
MutableByteArray# s -> Int# -> Int32# -> Int32# -> State# s -> (# State# s, Int32# #)
@@ -2168,8 +2183,9 @@ primop CasByteArrayOp_Int32 "casInt32Array#" GenPrimOp
value if the current value matches the provided old value. Returns
the value of the element before the operation. Implies a full memory
barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasByteArrayOp_Int64 "casInt64Array#" GenPrimOp
MutableByteArray# s -> Int# -> Int64# -> Int64# -> State# s -> (# State# s, Int64# #)
@@ -2178,56 +2194,63 @@ primop CasByteArrayOp_Int64 "casInt64Array#" GenPrimOp
value if the current value matches the provided old value. Returns
the value of the element before the operation. Implies a full memory
barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchAddByteArrayOp_Int "fetchAddIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to add,
atomically add the value to the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchSubByteArrayOp_Int "fetchSubIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to subtract,
atomically subtract the value from the element. Returns the value of
the element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchAndByteArrayOp_Int "fetchAndIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to AND,
atomically AND the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchNandByteArrayOp_Int "fetchNandIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to NAND,
atomically NAND the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchOrByteArrayOp_Int "fetchOrIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to OR,
atomically OR the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchXorByteArrayOp_Int "fetchXorIntArray#" GenPrimOp
MutableByteArray# s -> Int# -> Int# -> State# s -> (# State# s, Int# #)
{Given an array, and offset in machine words, and a value to XOR,
atomically XOR the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
------------------------------------------------------------------------
section "Addr#"
@@ -2273,15 +2296,17 @@ primop InterlockedExchange_Addr "atomicExchangeAddrAddr#" GenPrimOp
Addr# -> Addr# -> State# s -> (# State# s, Addr# #)
{The atomic exchange operation. Atomically exchanges the value at the first address
with the Addr# given as second argument. Implies a read barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop InterlockedExchange_Word "atomicExchangeWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{The atomic exchange operation. Atomically exchanges the value at the address
with the given value. Returns the old value. Implies a read barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Addr "atomicCasAddrAddr#" GenPrimOp
Addr# -> Addr# -> Addr# -> State# s -> (# State# s, Addr# #)
@@ -2294,8 +2319,9 @@ primop CasAddrOp_Addr "atomicCasAddrAddr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Word "atomicCasWordAddr#" GenPrimOp
Addr# -> Word# -> Word# -> State# s -> (# State# s, Word# #)
@@ -2308,8 +2334,9 @@ primop CasAddrOp_Word "atomicCasWordAddr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Word8 "atomicCasWord8Addr#" GenPrimOp
Addr# -> Word8# -> Word8# -> State# s -> (# State# s, Word8# #)
@@ -2322,8 +2349,9 @@ primop CasAddrOp_Word8 "atomicCasWord8Addr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Word16 "atomicCasWord16Addr#" GenPrimOp
Addr# -> Word16# -> Word16# -> State# s -> (# State# s, Word16# #)
@@ -2336,8 +2364,9 @@ primop CasAddrOp_Word16 "atomicCasWord16Addr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Word32 "atomicCasWord32Addr#" GenPrimOp
Addr# -> Word32# -> Word32# -> State# s -> (# State# s, Word32# #)
@@ -2350,8 +2379,9 @@ primop CasAddrOp_Word32 "atomicCasWord32Addr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop CasAddrOp_Word64 "atomicCasWord64Addr#" GenPrimOp
Addr# -> Word64# -> Word64# -> State# s -> (# State# s, Word64# #)
@@ -2364,68 +2394,77 @@ primop CasAddrOp_Word64 "atomicCasWord64Addr#" GenPrimOp
most architectures).
Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchAddAddrOp_Word "fetchAddWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to add,
atomically add the value to the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchSubAddrOp_Word "fetchSubWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to subtract,
atomically subtract the value from the element. Returns the value of
the element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchAndAddrOp_Word "fetchAndWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to AND,
atomically AND the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchNandAddrOp_Word "fetchNandWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to NAND,
atomically NAND the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchOrAddrOp_Word "fetchOrWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to OR,
atomically OR the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop FetchXorAddrOp_Word "fetchXorWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> (# State# s, Word# #)
{Given an address, and a value to XOR,
atomically XOR the value into the element. Returns the value of the
element before the operation. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop AtomicReadAddrOp_Word "atomicReadWordAddr#" GenPrimOp
Addr# -> State# s -> (# State# s, Word# #)
{Given an address, read a machine word. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
primop AtomicWriteAddrOp_Word "atomicWriteWordAddr#" GenPrimOp
Addr# -> Word# -> State# s -> State# s
{Given an address, write a machine word. Implies a full memory barrier.}
- with has_side_effects = True
- can_fail = True
+ with
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
------------------------------------------------------------------------
@@ -2441,18 +2480,18 @@ primop NewMutVarOp "newMutVar#" GenPrimOp
{Create 'MutVar#' with specified initial value in specified state thread.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
-- Note [Why MutVar# ops can't fail]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--
--- We don't label readMutVar# or writeMutVar# as can_fail.
+-- We don't label readMutVar# or writeMutVar# as CanFail.
-- This may seem a bit peculiar, because they surely *could*
-- fail spectacularly if passed a pointer to unallocated memory.
-- But MutVar#s are always correct by construction; we never
-- test if a pointer is valid before using it with these operations.
-- So we never have to worry about floating the pointer reference
--- outside a validity test. At the moment, has_side_effects blocks
+-- outside a validity test. At the moment, ReadWriteEffect blocks
-- up the relevant optimizations anyway, but we hope to draw finer
-- distinctions soon, which should improve matters for readMutVar#
-- at least.
@@ -2462,21 +2501,21 @@ primop ReadMutVarOp "readMutVar#" GenPrimOp
{Read contents of 'MutVar#'. Result is not yet evaluated.}
with
-- See Note [Why MutVar# ops can't fail]
- has_side_effects = True
+ effect = ReadWriteEffect
primop WriteMutVarOp "writeMutVar#" GenPrimOp
MutVar# s a_levpoly -> a_levpoly -> State# s -> State# s
{Write contents of 'MutVar#'.}
with
-- See Note [Why MutVar# ops can't fail]
- has_side_effects = True
+ effect = ReadWriteEffect
code_size = { primOpCodeSizeForeignCall } -- for the write barrier
primop AtomicSwapMutVarOp "atomicSwapMutVar#" GenPrimOp
MutVar# s a_levpoly -> a_levpoly -> State# s -> (# State# s, a_levpoly #)
{Atomically exchange the value of a 'MutVar#'.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
-- Note [Why not an unboxed tuple in atomicModifyMutVar2#?]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -2512,8 +2551,8 @@ primop AtomicModifyMutVar2Op "atomicModifyMutVar2#" GenPrimOp
well-typed high-level wrapper.}
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ -- Why was this previously can_fail?
strictness = { \ _arity -> mkClosedDmdSig [ topDmd, lazyApply1Dmd, topDmd ] topDiv }
primop AtomicModifyMutVar_Op "atomicModifyMutVar_#" GenPrimOp
@@ -2523,8 +2562,8 @@ primop AtomicModifyMutVar_Op "atomicModifyMutVar_#" GenPrimOp
previous contents. }
with
out_of_line = True
- has_side_effects = True
- can_fail = True
+ effect = ReadWriteEffect
+ -- Why was this previously can_fail?
strictness = { \ _arity -> mkClosedDmdSig [ topDmd, lazyApply1Dmd, topDmd ] topDiv }
primop CasMutVarOp "casMutVar#" GenPrimOp
@@ -2546,7 +2585,7 @@ primop CasMutVarOp "casMutVar#" GenPrimOp
}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
section "Exceptions"
@@ -2582,7 +2621,9 @@ primop CatchOp "catch#" GenPrimOp
, topDmd] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
+ -- Either inner computation might potentially raise an unchecked exception,
+ -- but it doesn't seem worth putting a WARNING in the haddocks over
primop RaiseOp "raise#" GenPrimOp
a_levpoly -> b_reppoly
@@ -2591,36 +2632,37 @@ primop RaiseOp "raise#" GenPrimOp
-- exceptions thrown by 'raise#' are considered *imprecise*.
-- See Note [Precise vs imprecise exceptions] in GHC.Types.Demand.
-- Hence, it has 'botDiv', not 'exnDiv'.
- -- For the same reasons, 'raise#' is marked as "can_fail" (which 'raiseIO#'
- -- is not), but not as "has_side_effects" (which 'raiseIO#' is).
- -- See Note [PrimOp can_fail and has_side_effects] in "GHC.Builtin.PrimOps".
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
- can_fail = True
+ effect = ThrowsException
+ work_free = True
primop RaiseUnderflowOp "raiseUnderflow#" GenPrimOp
(# #) -> b_reppoly
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
- can_fail = True
+ effect = ThrowsException
code_size = { primOpCodeSizeForeignCall }
+ work_free = True
primop RaiseOverflowOp "raiseOverflow#" GenPrimOp
(# #) -> b_reppoly
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
- can_fail = True
+ effect = ThrowsException
code_size = { primOpCodeSizeForeignCall }
+ work_free = True
primop RaiseDivZeroOp "raiseDivZero#" GenPrimOp
(# #) -> b_reppoly
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
- can_fail = True
+ effect = ThrowsException
code_size = { primOpCodeSizeForeignCall }
+ work_free = True
primop RaiseIOOp "raiseIO#" GenPrimOp
a_levpoly -> State# RealWorld -> (# State# RealWorld, b_reppoly #)
@@ -2629,7 +2671,8 @@ primop RaiseIOOp "raiseIO#" GenPrimOp
-- for why this is the *only* primop that has 'exnDiv'
strictness = { \ _arity -> mkClosedDmdSig [topDmd, topDmd] exnDiv }
out_of_line = True
- has_side_effects = True
+ effect = ThrowsException
+ work_free = True
primop MaskAsyncExceptionsOp "maskAsyncExceptions#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_reppoly #))
@@ -2644,7 +2687,7 @@ primop MaskAsyncExceptionsOp "maskAsyncExceptions#" GenPrimOp
strictness = { \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop MaskUninterruptibleOp "maskUninterruptible#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_reppoly #))
@@ -2658,7 +2701,7 @@ primop MaskUninterruptibleOp "maskUninterruptible#" GenPrimOp
with
strictness = { \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv }
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop UnmaskAsyncExceptionsOp "unmaskAsyncExceptions#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_reppoly #))
@@ -2673,13 +2716,13 @@ primop UnmaskAsyncExceptionsOp "unmaskAsyncExceptions#" GenPrimOp
strictness = { \ _arity -> mkClosedDmdSig [strictOnceApply1Dmd,topDmd] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop MaskStatus "getMaskingState#" GenPrimOp
State# RealWorld -> (# State# RealWorld, Int# #)
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
section "Continuations"
@@ -2849,7 +2892,7 @@ primop NewPromptTagOp "newPromptTag#" GenPrimOp
{ See "GHC.Prim#continuations". }
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop PromptOp "prompt#" GenPrimOp
PromptTag# a
@@ -2859,7 +2902,7 @@ primop PromptOp "prompt#" GenPrimOp
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd, strictOnceApply1Dmd, topDmd] topDiv }
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop Control0Op "control0#" GenPrimOp
PromptTag# a
@@ -2871,7 +2914,8 @@ primop Control0Op "control0#" GenPrimOp
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd, lazyApply2Dmd, topDmd] topDiv }
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
------------------------------------------------------------------------
section "STM-accessible Mutable Variables"
@@ -2886,7 +2930,7 @@ primop AtomicallyOp "atomically#" GenPrimOp
strictness = { \ _arity -> mkClosedDmdSig [strictManyApply1Dmd,topDmd] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
-- NB: retry#'s strictness information specifies it to diverge.
-- This lets the compiler perform some extra simplifications, since retry#
@@ -2903,7 +2947,7 @@ primop RetryOp "retry#" GenPrimOp
with
strictness = { \ _arity -> mkClosedDmdSig [topDmd] botDiv }
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop CatchRetryOp "catchRetry#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_levpoly #) )
@@ -2915,7 +2959,7 @@ primop CatchRetryOp "catchRetry#" GenPrimOp
, topDmd ] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop CatchSTMOp "catchSTM#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_levpoly #) )
@@ -2927,7 +2971,7 @@ primop CatchSTMOp "catchSTM#" GenPrimOp
, topDmd ] topDiv }
-- See Note [Strictness for mask/unmask/catch]
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop NewTVarOp "newTVar#" GenPrimOp
a_levpoly
@@ -2935,7 +2979,7 @@ primop NewTVarOp "newTVar#" GenPrimOp
{Create a new 'TVar#' holding a specified initial value.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ReadTVarOp "readTVar#" GenPrimOp
TVar# s a_levpoly
@@ -2945,7 +2989,7 @@ primop ReadTVarOp "readTVar#" GenPrimOp
Does not force evaluation of the result.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ReadTVarIOOp "readTVarIO#" GenPrimOp
TVar# s a_levpoly
@@ -2954,7 +2998,7 @@ primop ReadTVarIOOp "readTVarIO#" GenPrimOp
Does not force evaluation of the result.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop WriteTVarOp "writeTVar#" GenPrimOp
TVar# s a_levpoly
@@ -2963,7 +3007,7 @@ primop WriteTVarOp "writeTVar#" GenPrimOp
{Write contents of 'TVar#'.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
@@ -2981,7 +3025,7 @@ primop NewMVarOp "newMVar#" GenPrimOp
{Create new 'MVar#'; initially empty.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop TakeMVarOp "takeMVar#" GenPrimOp
MVar# s a_levpoly -> State# s -> (# State# s, a_levpoly #)
@@ -2989,7 +3033,7 @@ primop TakeMVarOp "takeMVar#" GenPrimOp
Then remove and return its contents, and set it empty.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop TryTakeMVarOp "tryTakeMVar#" GenPrimOp
MVar# s a_levpoly -> State# s -> (# State# s, Int#, a_levpoly #)
@@ -2997,7 +3041,7 @@ primop TryTakeMVarOp "tryTakeMVar#" GenPrimOp
Otherwise, return with integer 1 and contents of 'MVar#', and set 'MVar#' empty.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop PutMVarOp "putMVar#" GenPrimOp
MVar# s a_levpoly -> a_levpoly -> State# s -> State# s
@@ -3005,7 +3049,7 @@ primop PutMVarOp "putMVar#" GenPrimOp
Then store value arg as its new contents.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop TryPutMVarOp "tryPutMVar#" GenPrimOp
MVar# s a_levpoly -> a_levpoly -> State# s -> (# State# s, Int# #)
@@ -3013,7 +3057,7 @@ primop TryPutMVarOp "tryPutMVar#" GenPrimOp
Otherwise, store value arg as 'MVar#''s new contents, and return with integer 1.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ReadMVarOp "readMVar#" GenPrimOp
MVar# s a_levpoly -> State# s -> (# State# s, a_levpoly #)
@@ -3022,7 +3066,7 @@ primop ReadMVarOp "readMVar#" GenPrimOp
of intervention from other threads.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop TryReadMVarOp "tryReadMVar#" GenPrimOp
MVar# s a_levpoly -> State# s -> (# State# s, Int#, a_levpoly #)
@@ -3030,14 +3074,14 @@ primop TryReadMVarOp "tryReadMVar#" GenPrimOp
Otherwise, return with integer 1 and contents of 'MVar#'.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop IsEmptyMVarOp "isEmptyMVar#" GenPrimOp
MVar# s a_levpoly -> State# s -> (# State# s, Int# #)
{Return 1 if 'MVar#' is empty; 0 otherwise.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
@@ -3055,7 +3099,7 @@ primop NewIOPortOp "newIOPort#" GenPrimOp
{Create new 'IOPort#'; initially empty.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ReadIOPortOp "readIOPort#" GenPrimOp
IOPort# s a_levpoly -> State# s -> (# State# s, a_levpoly #)
@@ -3065,7 +3109,7 @@ primop ReadIOPortOp "readIOPort#" GenPrimOp
waiting to read this 'IOPort#'.}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop WriteIOPortOp "writeIOPort#" GenPrimOp
IOPort# s a_levpoly -> a_levpoly -> State# s -> (# State# s, Int# #)
@@ -3075,7 +3119,7 @@ primop WriteIOPortOp "writeIOPort#" GenPrimOp
and return with integer 1. }
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
section "Delay/wait operations"
@@ -3085,21 +3129,21 @@ primop DelayOp "delay#" GenPrimOp
Int# -> State# s -> State# s
{Sleep specified number of microseconds.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop WaitReadOp "waitRead#" GenPrimOp
Int# -> State# s -> State# s
{Block until input is available on specified file descriptor.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop WaitWriteOp "waitWrite#" GenPrimOp
Int# -> State# s -> State# s
{Block until output is possible on specified file descriptor.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
------------------------------------------------------------------------
@@ -3127,7 +3171,7 @@ primop ForkOp "fork#" GenPrimOp
(State# RealWorld -> (# State# RealWorld, a_reppoly #))
-> State# RealWorld -> (# State# RealWorld, ThreadId# #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
strictness = { \ _arity -> mkClosedDmdSig [ lazyApply1Dmd
, topDmd ] topDiv }
@@ -3136,7 +3180,7 @@ primop ForkOnOp "forkOn#" GenPrimOp
Int# -> (State# RealWorld -> (# State# RealWorld, a_reppoly #))
-> State# RealWorld -> (# State# RealWorld, ThreadId# #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
strictness = { \ _arity -> mkClosedDmdSig [ topDmd
, lazyApply1Dmd
@@ -3145,39 +3189,39 @@ primop ForkOnOp "forkOn#" GenPrimOp
primop KillThreadOp "killThread#" GenPrimOp
ThreadId# -> a -> State# RealWorld -> State# RealWorld
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop YieldOp "yield#" GenPrimOp
State# RealWorld -> State# RealWorld
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop MyThreadIdOp "myThreadId#" GenPrimOp
State# RealWorld -> (# State# RealWorld, ThreadId# #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
primop LabelThreadOp "labelThread#" GenPrimOp
ThreadId# -> ByteArray# -> State# RealWorld -> State# RealWorld
{Set the label of the given thread. The @ByteArray#@ should contain
a UTF-8-encoded string.}
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop IsCurrentThreadBoundOp "isCurrentThreadBound#" GenPrimOp
State# RealWorld -> (# State# RealWorld, Int# #)
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop NoDuplicateOp "noDuplicate#" GenPrimOp
State# s -> State# s
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop GetThreadLabelOp "threadLabel#" GenPrimOp
ThreadId# -> State# RealWorld -> (# State# RealWorld, Int#, ByteArray# #)
@@ -3202,7 +3246,7 @@ primop ThreadStatusOp "threadStatus#" GenPrimOp
@since 0.9}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
primop ListThreadsOp "listThreads#" GenPrimOp
State# RealWorld -> (# State# RealWorld, Array# ThreadId# #)
@@ -3213,7 +3257,7 @@ primop ListThreadsOp "listThreads#" GenPrimOp
@since 0.10}
with
out_of_line = True
- has_side_effects = True
+ effect = ReadWriteEffect
------------------------------------------------------------------------
section "Weak pointers"
@@ -3230,13 +3274,13 @@ primop MkWeakOp "mkWeak#" GenPrimOp
the type of @k@ must be represented by a pointer (i.e. of kind
@'TYPE' ''LiftedRep' or @'TYPE' ''UnliftedRep'@). }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop MkWeakNoFinalizerOp "mkWeakNoFinalizer#" GenPrimOp
a_levpoly -> b_levpoly -> State# RealWorld -> (# State# RealWorld, Weak# b_levpoly #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop AddCFinalizerToWeakOp "addCFinalizerToWeak#" GenPrimOp
@@ -3249,13 +3293,13 @@ primop AddCFinalizerToWeakOp "addCFinalizerToWeak#" GenPrimOp
@eptr@ and @ptr at . 'addCFinalizerToWeak#' returns
1 on success, or 0 if @w@ is already dead. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop DeRefWeakOp "deRefWeak#" GenPrimOp
Weak# a_levpoly -> State# RealWorld -> (# State# RealWorld, Int#, a_levpoly #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop FinalizeWeakOp "finalizeWeak#" GenPrimOp
@@ -3267,14 +3311,30 @@ primop FinalizeWeakOp "finalizeWeak#" GenPrimOp
action. An 'Int#' of @1@ indicates that the finalizer is valid. The
return value @b@ from the finalizer should be ignored. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop TouchOp "touch#" GenPrimOp
a_levpoly -> State# s -> State# s
with
- code_size = { 0 }
- has_side_effects = True
+ code_size = 0
+ effect = ReadWriteEffect -- see Note [touch# has ReadWriteEffect]
+ work_free = False
+
+
+-- Note [touch# has ReadWriteEffect]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Although touch# emits no code, it is marked as ReadWriteEffect to
+-- prevent it from being defeated by the optimizer:
+-- * Discarding a touch# call would defeat its whole purpose.
+-- * Strictly floating a touch# call out would shorten the lifetime
+-- of the touched object, again defeating its purpose.
+-- * Duplicating a touch# call might unpredictably extend the lifetime
+-- of the touched object. Although this would not defeat the purpose
+-- of touch#, it seems undesirable.
+--
+-- In practice, this designation probably doesn't matter in most cases,
+-- as touch# is usually tightly coupled with a "real" read or write effect.
------------------------------------------------------------------------
section "Stable pointers and names"
@@ -3287,24 +3347,24 @@ primtype StableName# a
primop MakeStablePtrOp "makeStablePtr#" GenPrimOp
a_levpoly -> State# RealWorld -> (# State# RealWorld, StablePtr# a_levpoly #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop DeRefStablePtrOp "deRefStablePtr#" GenPrimOp
StablePtr# a_levpoly -> State# RealWorld -> (# State# RealWorld, a_levpoly #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop EqStablePtrOp "eqStablePtr#" GenPrimOp
StablePtr# a_levpoly -> StablePtr# a_levpoly -> Int#
with
- has_side_effects = True
+ effect = ReadWriteEffect
primop MakeStableNameOp "makeStableName#" GenPrimOp
a_levpoly -> State# RealWorld -> (# State# RealWorld, StableName# a_levpoly #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop StableNameToIntOp "stableNameToInt#" GenPrimOp
@@ -3336,7 +3396,7 @@ primop CompactNewOp "compactNew#" GenPrimOp
The capacity is rounded up to a multiple of the allocator block size
and is capped to one mega block. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactResizeOp "compactResize#" GenPrimOp
@@ -3346,7 +3406,7 @@ primop CompactResizeOp "compactResize#" GenPrimOp
determines the capacity of each compact block in the CNF. It
does not retroactively affect existing compact blocks in the CNF. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactContainsOp "compactContains#" GenPrimOp
@@ -3388,7 +3448,7 @@ primop CompactAllocateBlockOp "compactAllocateBlock#" GenPrimOp
so that the address does not escape or memory will be leaked.
}
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactFixupPointersOp "compactFixupPointers#" GenPrimOp
@@ -3401,7 +3461,7 @@ primop CompactFixupPointersOp "compactFixupPointers#" GenPrimOp
a serialized CNF. It returns the new CNF and the new adjusted
root address. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactAdd "compactAdd#" GenPrimOp
@@ -3414,7 +3474,7 @@ primop CompactAdd "compactAdd#" GenPrimOp
enforce any mutual exclusion; the caller is expected to
arrange this. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactAddWithSharing "compactAddWithSharing#" GenPrimOp
@@ -3422,7 +3482,7 @@ primop CompactAddWithSharing "compactAddWithSharing#" GenPrimOp
{ Like 'compactAdd#', but retains sharing and cycles
during compaction. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop CompactSize "compactSize#" GenPrimOp
@@ -3430,7 +3490,7 @@ primop CompactSize "compactSize#" GenPrimOp
{ Return the total capacity (in bytes) of all the compact blocks
in the CNF. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
------------------------------------------------------------------------
@@ -3442,7 +3502,8 @@ primop ReallyUnsafePtrEqualityOp "reallyUnsafePtrEquality#" GenPrimOp
a_levpoly -> b_levpoly -> Int#
{ Returns @1#@ if the given pointers are equal and @0#@ otherwise. }
with
- can_fail = True -- See Note [reallyUnsafePtrEquality# can_fail]
+ effect = CanFail -- See Note [reallyUnsafePtrEquality# CanFail]
+ can_fail_warning = DoNotWarnCanFail
-- Note [Pointer comparison operations]
-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
@@ -3474,7 +3535,7 @@ primop ReallyUnsafePtrEqualityOp "reallyUnsafePtrEquality#" GenPrimOp
--
-- (PE5) reallyUnsafePtrEquality# can't fail, but it is marked as such
-- to prevent it from floating out.
--- See Note [reallyUnsafePtrEquality# can_fail]
+-- See Note [reallyUnsafePtrEquality# CanFail]
--
-- The library GHC.Prim.PtrEq (and GHC.Exts) provides
--
@@ -3509,10 +3570,10 @@ primop ReallyUnsafePtrEqualityOp "reallyUnsafePtrEquality#" GenPrimOp
--
-- These operations are all specialisations of unsafePtrEquality#.
--- Note [reallyUnsafePtrEquality# can_fail]
--- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+-- Note [reallyUnsafePtrEquality# CanFail]
+-- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-- reallyUnsafePtrEquality# can't actually fail, per se, but we mark it
--- can_fail anyway. Until 5a9a1738023a, GHC considered primops okay for
+-- CanFail anyway. Until 5a9a1738023a, GHC considered primops okay for
-- speculation only when their arguments were known to be forced. This was
-- unnecessarily conservative, but it prevented reallyUnsafePtrEquality# from
-- floating out of places where its arguments were known to be forced.
@@ -3547,30 +3608,33 @@ primop ParOp "par#" GenPrimOp
with
-- Note that Par is lazy to avoid that the sparked thing
-- gets evaluated strictly, which it should *not* be
- has_side_effects = True
+ effect = ReadWriteEffect
code_size = { primOpCodeSizeForeignCall }
deprecated_msg = { Use 'spark#' instead }
primop SparkOp "spark#" GenPrimOp
a -> State# s -> (# State# s, a #)
- with has_side_effects = True
+ with effect = ReadWriteEffect
code_size = { primOpCodeSizeForeignCall }
+-- See Note [seq# magic] in GHC.Core.Op.ConstantFold
primop SeqOp "seq#" GenPrimOp
a -> State# s -> (# State# s, a #)
- -- See Note [seq# magic] in GHC.Core.Op.ConstantFold
+ with
+ effect = ThrowsException
+ work_free = True -- seq# does work iff its lifted arg does work
primop GetSparkOp "getSpark#" GenPrimOp
State# s -> (# State# s, Int#, a #)
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop NumSparks "numSparks#" GenPrimOp
State# s -> (# State# s, Int# #)
{ Returns the number of sparks in the local spark pool. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
@@ -3592,6 +3656,8 @@ primop KeepAliveOp "keepAlive#" GenPrimOp
with
out_of_line = True
strictness = { \ _arity -> mkClosedDmdSig [topDmd, topDmd, strictOnceApply1Dmd] topDiv }
+ effect = ReadWriteEffect
+ -- The invoked computation may have side effects
------------------------------------------------------------------------
@@ -3600,16 +3666,20 @@ section "Tag to enum stuff"
and small integers.}
------------------------------------------------------------------------
+-- See Note [dataToTag# magic] in GHC.Core.Opt.ConstantFold
primop DataToTagOp "dataToTag#" GenPrimOp
a -> Int# -- Zero-indexed; the first constructor has tag zero
{ Evaluates the argument and returns the tag of the result.
Tags are Zero-indexed; the first constructor has tag zero. }
with
strictness = { \ _arity -> mkClosedDmdSig [evalDmd] topDiv }
- -- See Note [dataToTag# magic] in GHC.Core.Opt.ConstantFold
+ effect = ThrowsException
+ cheap = True
primop TagToEnumOp "tagToEnum#" GenPrimOp
Int# -> a
+ with
+ effect = CanFail
------------------------------------------------------------------------
section "Bytecode operations"
@@ -3658,7 +3728,7 @@ primop NewBCOOp "newBCO#" GenPrimOp
encoded in @instrs@, and a static reference table usage bitmap given by
@bitmap at . }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop UnpackClosureOp "unpackClosure#" GenPrimOp
@@ -3784,7 +3854,7 @@ primop TraceEventOp "traceEvent#" GenPrimOp
argument. The event will be emitted either to the @.eventlog@ file,
or to stderr, depending on the runtime RTS flags. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop TraceEventBinaryOp "traceBinaryEvent#" GenPrimOp
@@ -3794,7 +3864,7 @@ primop TraceEventBinaryOp "traceBinaryEvent#" GenPrimOp
the given length passed as the second argument. The event will be
emitted to the @.eventlog@ file. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop TraceMarkerOp "traceMarker#" GenPrimOp
@@ -3804,14 +3874,14 @@ primop TraceMarkerOp "traceMarker#" GenPrimOp
argument. The event will be emitted either to the @.eventlog@ file,
or to stderr, depending on the runtime RTS flags. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primop SetThreadAllocationCounter "setThreadAllocationCounter#" GenPrimOp
Int64# -> State# RealWorld -> State# RealWorld
{ Sets the allocation counter for the current thread to the given value. }
with
- has_side_effects = True
+ effect = ReadWriteEffect
out_of_line = True
primtype StackSnapshot#
@@ -3900,7 +3970,7 @@ primop VecUnpackOp "unpack#" GenPrimOp
primop VecInsertOp "insert#" GenPrimOp
VECTOR -> SCALAR -> Int# -> VECTOR
{ Insert a scalar at the given position in a vector. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
@@ -3927,21 +3997,21 @@ primop VecMulOp "times#" GenPrimOp
primop VecDivOp "divide#" GenPrimOp
VECTOR -> VECTOR -> VECTOR
{ Divide two vectors element-wise. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = FLOAT_VECTOR_TYPES
primop VecQuotOp "quot#" GenPrimOp
VECTOR -> VECTOR -> VECTOR
{ Rounds towards zero element-wise. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = INT_VECTOR_TYPES
primop VecRemOp "rem#" GenPrimOp
VECTOR -> VECTOR -> VECTOR
{ Satisfies @('quot#' x y) 'times#' y 'plus#' ('rem#' x y) == x at . }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = INT_VECTOR_TYPES
@@ -3954,46 +4024,46 @@ primop VecNegOp "negate#" GenPrimOp
primop VecIndexByteArrayOp "indexArray#" GenPrimOp
ByteArray# -> Int# -> VECTOR
{ Read a vector from specified index of immutable array. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecReadByteArrayOp "readArray#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> (# State# s, VECTOR #)
{ Read a vector from specified index of mutable array. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecWriteByteArrayOp "writeArray#" GenPrimOp
MutableByteArray# s -> Int# -> VECTOR -> State# s -> State# s
{ Write a vector to specified index of mutable array. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecIndexOffAddrOp "indexOffAddr#" GenPrimOp
Addr# -> Int# -> VECTOR
{ Reads vector; offset in bytes. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecReadOffAddrOp "readOffAddr#" GenPrimOp
Addr# -> Int# -> State# s -> (# State# s, VECTOR #)
{ Reads vector; offset in bytes. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecWriteOffAddrOp "writeOffAddr#" GenPrimOp
Addr# -> Int# -> VECTOR -> State# s -> State# s
{ Write vector; offset in bytes. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
@@ -4001,46 +4071,46 @@ primop VecWriteOffAddrOp "writeOffAddr#" GenPrimOp
primop VecIndexScalarByteArrayOp "indexArrayAs#" GenPrimOp
ByteArray# -> Int# -> VECTOR
{ Read a vector from specified index of immutable array of scalars; offset is in scalar elements. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecReadScalarByteArrayOp "readArrayAs#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> (# State# s, VECTOR #)
{ Read a vector from specified index of mutable array of scalars; offset is in scalar elements. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecWriteScalarByteArrayOp "writeArrayAs#" GenPrimOp
MutableByteArray# s -> Int# -> VECTOR -> State# s -> State# s
{ Write a vector to specified index of mutable array of scalars; offset is in scalar elements. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecIndexScalarOffAddrOp "indexOffAddrAs#" GenPrimOp
Addr# -> Int# -> VECTOR
{ Reads vector; offset in scalar elements. }
- with can_fail = True
+ with effect = CanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecReadScalarOffAddrOp "readOffAddrAs#" GenPrimOp
Addr# -> Int# -> State# s -> (# State# s, VECTOR #)
{ Reads vector; offset in scalar elements. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
primop VecWriteScalarOffAddrOp "writeOffAddrAs#" GenPrimOp
Addr# -> Int# -> VECTOR -> State# s -> State# s
{ Write vector; offset in scalar elements. }
- with has_side_effects = True
- can_fail = True
+ with effect = ReadWriteEffect
+ can_fail_warning = YesWarnCanFail
llvm_only = True
vector = ALL_VECTOR_TYPES
@@ -4089,7 +4159,7 @@ section "Prefetch"
It is important to note that while the prefetch operations will never change the
answer to a pure computation, They CAN change the memory locations resident
in a CPU cache and that may change the performance and timing characteristics
- of an application. The prefetch operations are marked has_side_effects=True
+ of an application. The prefetch operations are marked as ReadWriteEffect
to reflect that these operations have side effects with respect to the runtime
performance characteristics of the resulting code. Additionally, if the prefetchValue
operations did not have this attribute, GHC does a float out transformation that
@@ -4106,70 +4176,70 @@ section "Prefetch"
---
primop PrefetchByteArrayOp3 "prefetchByteArray3#" GenPrimOp
ByteArray# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchMutableByteArrayOp3 "prefetchMutableByteArray3#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchAddrOp3 "prefetchAddr3#" GenPrimOp
Addr# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchValueOp3 "prefetchValue3#" GenPrimOp
a -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
----
primop PrefetchByteArrayOp2 "prefetchByteArray2#" GenPrimOp
ByteArray# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchMutableByteArrayOp2 "prefetchMutableByteArray2#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchAddrOp2 "prefetchAddr2#" GenPrimOp
Addr# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchValueOp2 "prefetchValue2#" GenPrimOp
a -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
----
primop PrefetchByteArrayOp1 "prefetchByteArray1#" GenPrimOp
ByteArray# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchMutableByteArrayOp1 "prefetchMutableByteArray1#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchAddrOp1 "prefetchAddr1#" GenPrimOp
Addr# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchValueOp1 "prefetchValue1#" GenPrimOp
a -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
----
primop PrefetchByteArrayOp0 "prefetchByteArray0#" GenPrimOp
ByteArray# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchMutableByteArrayOp0 "prefetchMutableByteArray0#" GenPrimOp
MutableByteArray# s -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchAddrOp0 "prefetchAddr0#" GenPrimOp
Addr# -> Int# -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
primop PrefetchValueOp0 "prefetchValue0#" GenPrimOp
a -> State# s -> State# s
- with has_side_effects = True
+ with effect = ReadWriteEffect
-- Note [RuntimeRep polymorphism in continuation-style primops]
=====================================
compiler/GHC/Core.hs
=====================================
@@ -427,9 +427,6 @@ which will generate a @case@ if necessary
The let-can-float invariant is initially enforced by mkCoreLet in GHC.Core.Make.
-For discussion of some implications of the let-can-float invariant primops see
-Note [Checking versus non-checking primops] in GHC.Builtin.PrimOps.
-
Historical Note [The let/app invariant]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
Before 2022 GHC used the "let/app invariant", which applied the let-can-float rules
=====================================
compiler/GHC/Core/Opt/ConstantFold.hs
=====================================
@@ -1099,7 +1099,7 @@ shiftRule lit_num_ty shift_op = do
_ | shift_len == 0 -> pure e1
-- See Note [Guarding against silly shifts]
- _ | shift_len < 0 || shift_len > bit_size
+ _ | shift_len < 0 || shift_len >= bit_size
-> pure $ Lit $ mkLitNumberWrap platform lit_num_ty 0
-- Be sure to use lit_num_ty here, so we get a correctly typed zero.
-- See #18589
@@ -1581,15 +1581,15 @@ as follows:
let x = I# (error "invalid shift")
in ...
-This was originally done in the fix to #16449 but this breaks the let-can-float
-invariant (see Note [Core let-can-float invariant] in GHC.Core) as noted in #16742.
-For the reasons discussed in Note [Checking versus non-checking
-primops] (in the PrimOp module) there is no safe way to rewrite the argument of I#
-such that it bottoms.
+This was originally done in the fix to #16449 but this breaks the
+let-can-float invariant (see Note [Core let-can-float invariant] in
+GHC.Core) as noted in #16742. For the reasons discussed under
+"NoEffect" in Note [Classifying primop effects] (in GHC.Builtin.PrimOps)
+there is no safe way to rewrite the argument of I# such that it bottoms.
-Consequently we instead take advantage of the fact that large shifts are
-undefined behavior (see associated documentation in primops.txt.pp) and
-transform the invalid shift into an "obviously incorrect" value.
+Consequently we instead take advantage of the fact that the result of a
+large shift is unspecified (see associated documentation in primops.txt.pp)
+and transform the invalid shift into an "obviously incorrect" value.
There are two cases:
@@ -2016,13 +2016,6 @@ Only `SeqOp` shares that property. (Other primops do not do anything
as fancy as argument evaluation.) The special handling for dataToTag#
is:
-* GHC.Core.Utils.exprOkForSpeculation has a special case for DataToTagOp,
- (actually in app_ok). Most primops with lifted arguments do not
- evaluate those arguments, but DataToTagOp and SeqOp are two
- exceptions. We say that they are /never/ ok-for-speculation,
- regardless of the evaluated-ness of their argument.
- See GHC.Core.Utils Note [exprOkForSpeculation and SeqOp/DataToTagOp]
-
* There is a special case for DataToTagOp in GHC.StgToCmm.Expr.cgExpr,
that evaluates its argument and then extracts the tag from
the returned value.
@@ -2113,12 +2106,9 @@ Implementing seq#. The compiler has magic for SeqOp in
- GHC.StgToCmm.Expr.cgExpr, and cgCase: special case for seq#
-- GHC.Core.Utils.exprOkForSpeculation;
- see Note [exprOkForSpeculation and SeqOp/DataToTagOp] in GHC.Core.Utils
-
- Simplify.addEvals records evaluated-ness for the result; see
Note [Adding evaluatedness info to pattern-bound variables]
- in GHC.Core.Opt.Simplify
+ in GHC.Core.Opt.Simplify.Iteration
-}
seqRule :: RuleM CoreExpr
=====================================
compiler/GHC/Core/Opt/FloatIn.hs
=====================================
@@ -423,6 +423,30 @@ motivating example was #5658: in particular, this change allows
array indexing operations, which have a single DEFAULT alternative
without any binders, to be floated inward.
+In particular, we want to be able to transform
+
+ case indexIntArray# arr i of vi {
+ __DEFAULT -> case <# j n of _ {
+ __DEFAULT -> False
+ 1# -> case indexIntArray# arr j of vj {
+ __DEFAULT -> ... vi ... vj ...
+ }
+ }
+ }
+
+by floating in `indexIntArray# arr i` to produce
+
+ case <# j n of _ {
+ __DEFAULT -> False
+ 1# -> case indexIntArray# arr i of vi {
+ __DEFAULT -> case indexIntArray# arr j of vj {
+ __DEFAULT -> ... vi ... vj ...
+ }
+ }
+ }
+
+...which skips the `indexIntArray# arr i` call entirely in the out-of-bounds branch.
+
SIMD primops for unpacking SIMD vectors into an unboxed tuple of unboxed
scalars also need to be floated inward, but unpacks have a single non-DEFAULT
alternative that binds the elements of the tuple. We now therefore also support
@@ -431,12 +455,11 @@ floating in cases with a single alternative that may bind values.
But there are wrinkles
* Which unlifted cases do we float?
- See Note [PrimOp can_fail and has_side_effects] in GHC.Builtin.PrimOps which
- explains:
- - We can float in can_fail primops (which concerns imprecise exceptions),
- but we can't float them out.
- - But we can float a has_side_effects primop, but NOT inside a lambda,
- so for now we don't float them at all. Hence exprOkForSideEffects.
+ See Note [Transformations affected by primop effects] in GHC.Builtin.PrimOps
+ which explains:
+ - We can float in or discard CanFail primops, but we can't float them out.
+ - We don't want to discard a synchronous exception or side effect
+ so we don't float those at all. Hence exprOkToDiscard.
- Throwing precise exceptions is a special case of the previous point: We
may /never/ float in a call to (something that ultimately calls)
'raiseIO#'.
@@ -448,7 +471,7 @@ But there are wrinkles
===>
f (case a /# b of r -> F# r)
because that creates a new thunk that wasn't there before. And
- because it can't be floated out (can_fail), the thunk will stay
+ because it can't be floated out (CanFail), the thunk will stay
there. Disaster! (This happened in nofib 'simple' and 'scs'.)
Solution: only float cases into the branches of other cases, and
@@ -477,7 +500,7 @@ bindings are:
fiExpr platform to_drop (_, AnnCase scrut case_bndr _ [AnnAlt con alt_bndrs rhs])
| isUnliftedType (idType case_bndr)
-- binders have a fixed RuntimeRep so it's OK to call isUnliftedType
- , exprOkForSideEffects (deAnnotate scrut)
+ , exprOkToDiscard (deAnnotate scrut)
-- See Note [Floating primops]
= wrapFloats shared_binds $
fiExpr platform (case_float : rhs_binds) rhs
=====================================
compiler/GHC/Core/Opt/SetLevels.hs
=====================================
@@ -15,7 +15,7 @@
outwards (@FloatOut@).
2. We also let-ify many expressions (notably case scrutinees), so they
- will have a fighting chance of being floated sensible.
+ will have a fighting chance of being floated sensibly.
3. Note [Need for cloning during float-out]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
=====================================
compiler/GHC/Core/Opt/Simplify/Iteration.hs
=====================================
@@ -3016,11 +3016,10 @@ rebuildCase env scrut case_bndr alts@[Alt _ bndrs rhs] cont
-- a) it binds nothing (so it's really just a 'seq')
-- b) evaluating the scrutinee has no side effects
| is_plain_seq
- , exprOkForSideEffects scrut
+ , exprOkToDiscard scrut
-- The entire case is dead, so we can drop it
-- if the scrutinee converges without having imperative
-- side effects or raising a Haskell exception
- -- See Note [PrimOp can_fail and has_side_effects] in GHC.Builtin.PrimOps
= simplExprF env rhs cont
-- 2b. Turn the case into a let, if
=====================================
compiler/GHC/Core/Utils.hs
=====================================
@@ -27,7 +27,7 @@ module GHC.Core.Utils (
exprIsDupable, exprIsTrivial, getIdFromTrivialExpr,
getIdFromTrivialExpr_maybe,
exprIsCheap, exprIsExpandable, exprIsCheapX, CheapAppFun,
- exprIsHNF, exprOkForSpeculation, exprOkForSideEffects, exprOkForSpecEval,
+ exprIsHNF, exprOkForSpeculation, exprOkToDiscard, exprOkForSpecEval,
exprIsWorkFree, exprIsConLike,
isCheapApp, isExpandableApp, isSaturatedConApp,
exprIsTickedString, exprIsTickedString_maybe,
@@ -1381,6 +1381,7 @@ isWorkFreeApp fn n_val_args
| otherwise
= case idDetails fn of
DataConWorkId {} -> True
+ PrimOpId op _ -> primOpIsWorkFree op
_ -> False
isCheapApp :: CheapAppFun
@@ -1488,34 +1489,45 @@ it's applied only to dictionaries.
-}
-----------------------------
--- | 'exprOkForSpeculation' returns True of an expression that is:
+-- | To a first approximation, 'exprOkForSpeculation' returns True of
+-- an expression that is:
--
-- * Safe to evaluate even if normal order eval might not
--- evaluate the expression at all, or
+-- evaluate the expression at all, and
--
-- * Safe /not/ to evaluate even if normal order would do so
--
--- It is usually called on arguments of unlifted type, but not always
--- In particular, Simplify.rebuildCase calls it on lifted types
--- when a 'case' is a plain 'seq'. See the example in
--- Note [exprOkForSpeculation: case expressions] below
+-- More specifically, this means that:
+-- * A: Evaluation of the expression reaches weak-head-normal-form,
+-- * B: soon,
+-- * C: without causing a write side effect (e.g. writing a mutable variable).
--
--- Precisely, it returns @True@ iff:
--- a) The expression guarantees to terminate,
--- b) soon,
--- c) without causing a write side effect (e.g. writing a mutable variable)
--- d) without throwing a Haskell exception
--- e) without risking an unchecked runtime exception (array out of bounds,
--- divide by zero)
+-- In particular, an expression that may
+-- * throw a synchronous Haskell exception, or
+-- * risk an unchecked runtime exception (e.g. array
+-- out of bounds, divide by zero)
+-- is /not/ considered OK-for-speculation, as these violate condition A.
--
--- For @exprOkForSideEffects@ the list is the same, but omitting (e).
+-- For 'exprOkToDiscard', condition A is weakened to allow expressions
+-- that might risk an unchecked runtime exception but must otherwise
+-- reach weak-head-normal-form.
+-- (Note that 'exprOkForSpeculation' implies 'exprOkToDiscard')
--
--- Note that
--- exprIsHNF implies exprOkForSpeculation
--- exprOkForSpeculation implies exprOkForSideEffects
+-- But in fact both functions are a bit more conservative than the above,
+-- in at least the following ways:
+--
+-- * W1: We do not take advantage of already-evaluated lifted variables.
+-- As a result, 'exprIsHNF' DOES NOT imply 'exprOkForSpeculation';
+-- if @y@ is a case-binder of lifted type, then @exprIsHNF y@ is
+-- 'True', while @exprOkForSpeculation y@ is 'False'.
+-- See Note [exprOkForSpeculation and evaluated variables] for why.
+-- * W2: Read-effects on mutable variables are currently also included.
+-- See Note [Classifying primop effects] "GHC.Builtin.PrimOps".
+-- * W3: Currently, 'exprOkForSpeculation' always returns 'False' for
+-- let-expressions. Lets can be stacked deeply, so we just give up.
+-- In any case, the argument of 'exprOkForSpeculation' is usually in
+-- a strict context, so any lets will have been floated away.
--
--- See Note [PrimOp can_fail and has_side_effects] in "GHC.Builtin.PrimOps"
--- and Note [Transformations affected by can_fail and has_side_effects]
--
-- As an example of the considerations in this test, consider:
--
@@ -1529,12 +1541,24 @@ it's applied only to dictionaries.
-- > in E
-- > }
--
--- We can only do this if the @y + 1@ is ok for speculation: it has no
+-- We can only do this if the @y# +# 1#@ is ok for speculation: it has no
-- side effects, and can't diverge or raise an exception.
+--
+--
+-- See also Note [Classifying primop effects] in "GHC.Builtin.PrimOps"
+-- and Note [Transformations affected by primop effects].
+--
+-- 'exprOkForSpeculation' is used to define Core's let-can-float
+-- invariant. (See Note [Core let-can-float invariant] in
+-- "GHC.Core".) It is therefore frequently called on arguments of
+-- unlifted type, especially via 'needsCaseBinding'. But it is
+-- sometimes called on expressions of lifted type as well. For
+-- example, see Note [Speculative evaluation] in "GHC.CoreToStg.Prep".
+
-exprOkForSpeculation, exprOkForSideEffects :: CoreExpr -> Bool
+exprOkForSpeculation, exprOkToDiscard :: CoreExpr -> Bool
exprOkForSpeculation = expr_ok fun_always_ok primOpOkForSpeculation
-exprOkForSideEffects = expr_ok fun_always_ok primOpOkForSideEffects
+exprOkToDiscard = expr_ok fun_always_ok primOpOkToDiscard
fun_always_ok :: Id -> Bool
fun_always_ok _ = True
@@ -1564,10 +1588,7 @@ expr_ok fun_ok primop_ok (Tick tickish e)
| otherwise = expr_ok fun_ok primop_ok e
expr_ok _ _ (Let {}) = False
- -- Lets can be stacked deeply, so just give up.
- -- In any case, the argument of exprOkForSpeculation is
- -- usually in a strict context, so any lets will have been
- -- floated away.
+-- See W3 in the Haddock comment for exprOkForSpeculation
expr_ok fun_ok primop_ok (Case scrut bndr _ alts)
= -- See Note [exprOkForSpeculation: case expressions]
@@ -1599,16 +1620,24 @@ app_ok :: (Id -> Bool) -> (PrimOp -> Bool) -> Id -> [CoreExpr] -> Bool
app_ok fun_ok primop_ok fun args
| not (fun_ok fun)
= False -- This code path is only taken for Note [Speculative evaluation]
+
+ | idArity fun > n_val_args
+ -- Partial application: just check passing the arguments is OK
+ = args_ok
+
| otherwise
= case idDetails fun of
- DFunId new_type -> not new_type
+ DFunId new_type -> not new_type
-- DFuns terminate, unless the dict is implemented
-- with a newtype in which case they may not
- DataConWorkId {} -> True
+ DataConWorkId {} -> args_ok
-- The strictness of the constructor has already
-- been expressed by its "wrapper", so we don't need
-- to take the arguments into account
+ -- Well, we thought so. But it's definitely wrong!
+ -- See #20749 and Note [How untagged pointers can
+ -- end up in strict fields] in GHC.Stg.InferTags
ClassOpId _ is_terminating_result
| is_terminating_result -- See Note [exprOkForSpeculation and type classes]
@@ -1630,16 +1659,7 @@ app_ok fun_ok primop_ok fun args
-- Often there is a literal divisor, and this
-- can get rid of a thunk in an inner loop
- | SeqOp <- op -- See Note [exprOkForSpeculation and SeqOp/DataToTagOp]
- -> False -- for the special cases for SeqOp and DataToTagOp
- | DataToTagOp <- op
- -> False
- | KeepAliveOp <- op
- -> False
-
- | otherwise
- -> primop_ok op -- Check the primop itself
- && and (zipWith arg_ok arg_tys args) -- Check the arguments
+ | otherwise -> primop_ok op && args_ok
_other -- Unlifted and terminating types;
-- Also c.f. the Var case of exprIsHNF
@@ -1652,10 +1672,6 @@ app_ok fun_ok primop_ok fun args
-- (If we added unlifted function types this would change,
-- and we'd need to actually test n_val_args == 0.)
- -- Partial applications
- | idArity fun > n_val_args ->
- and (zipWith arg_ok arg_tys args) -- Check the arguments
-
-- Functions that terminate fast without raising exceptions etc
-- See Note [Discarding unnecessary unsafeEqualityProofs]
| fun `hasKey` unsafeEqualityProofIdKey -> True
@@ -1669,12 +1685,14 @@ app_ok fun_ok primop_ok fun args
n_val_args = valArgCount args
(arg_tys, _) = splitPiTys fun_ty
- -- Used for arguments to primops and to partial applications
+ -- Even if a function call itself is OK, any unlifted
+ -- args are still evaluated eagerly and must be checked
+ args_ok = and (zipWith arg_ok arg_tys args)
arg_ok :: PiTyVarBinder -> CoreExpr -> Bool
arg_ok (Named _) _ = True -- A type argument
arg_ok (Anon ty _) arg -- A term argument
| definitelyLiftedType (scaledThing ty)
- = True -- See Note [Primops with lifted arguments]
+ = True -- lifted args are not evaluated eagerly
| otherwise
= expr_ok fun_ok primop_ok arg
@@ -1821,46 +1839,16 @@ points do the job nicely.
------- End of historical note ------------
-Note [Primops with lifted arguments]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Is this ok-for-speculation (see #13027)?
- reallyUnsafePtrEquality# a b
-Well, yes. The primop accepts lifted arguments and does not
-evaluate them. Indeed, in general primops are, well, primitive
-and do not perform evaluation.
-
-Bottom line:
- * In exprOkForSpeculation we simply ignore all lifted arguments.
- * In the rare case of primops that /do/ evaluate their arguments,
- (namely DataToTagOp and SeqOp) return False; see
- Note [exprOkForSpeculation and evaluated variables]
-
-Note [exprOkForSpeculation and SeqOp/DataToTagOp]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Most primops with lifted arguments don't evaluate them
-(see Note [Primops with lifted arguments]), so we can ignore
-that argument entirely when doing exprOkForSpeculation.
-
-But DataToTagOp and SeqOp are exceptions to that rule.
-For reasons described in Note [exprOkForSpeculation and
-evaluated variables], we simply return False for them.
-
-Not doing this made #5129 go bad.
-Lots of discussion in #15696.
-
Note [exprOkForSpeculation and evaluated variables]
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-Recall that
- seq# :: forall a s. a -> State# s -> (# State# s, a #)
- dataToTag# :: forall a. a -> Int#
-must always evaluate their first argument.
-
-Now consider these examples:
+Consider these examples:
* case x of y { DEFAULT -> ....y.... }
Should 'y' (alone) be considered ok-for-speculation?
* case x of y { DEFAULT -> ....let z = dataToTag# y... }
- Should (dataToTag# y) be considered ok-for-spec?
+ Should (dataToTag# y) be considered ok-for-spec? Recall that
+ dataToTag# :: forall a. a -> Int#
+ must always evaluate its argument. (See also Note [dataToTag# magic].)
You could argue 'yes', because in the case alternative we know that
'y' is evaluated. But the binder-swap transformation, which is
@@ -1874,13 +1862,16 @@ and then it really, really doesn't obey the let-can-float invariant.
The solution is simple: exprOkForSpeculation does not try to take
advantage of the evaluated-ness of (lifted) variables. And it returns
-False (always) for DataToTagOp and SeqOp.
+False (always) for primops that perform evaluation. We achieve the latter
+by marking the relevant primops as "ThrowsException" or
+"ReadWriteEffect"; see also Note [Classifying primop effects] in
+GHC.Builtin.PrimOps.
Note that exprIsHNF /can/ and does take advantage of evaluated-ness;
it doesn't have the trickiness of the let-can-float invariant to worry about.
Note [Discarding unnecessary unsafeEqualityProofs]
-~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In #20143 we found
case unsafeEqualityProof @t1 @t2 of UnsafeRefl cv[dead] -> blah
where 'blah' didn't mention 'cv'. We'd like to discard this
@@ -1889,7 +1880,7 @@ To do this we need to know
(a) that cv is unused (done by OccAnal), and
(b) that unsafeEqualityProof terminates rapidly without side effects.
-At the moment we check that explicitly here in exprOkForSideEffects,
+At the moment we check that explicitly here in exprOkToDiscard,
but one might imagine a more systematic check in future.
@@ -1904,15 +1895,15 @@ but one might imagine a more systematic check in future.
-- ~~~~~~~~~~~~~~~~
-- | exprIsHNF returns true for expressions that are certainly /already/
-- evaluated to /head/ normal form. This is used to decide whether it's ok
--- to change:
+-- to perform case-to-let for lifted expressions, which changes:
--
--- > case x of _ -> e
+-- > case x of x' { _ -> e }
--
-- into:
--
--- > e
+-- > let x' = x in e
--
--- and to decide whether it's safe to discard a 'seq'.
+-- and in so doing makes the binding lazy.
--
-- So, it does /not/ treat variables as evaluated, unless they say they are.
-- However, it /does/ treat partial applications and constructor applications
=====================================
compiler/GHC/Types/Demand.hs
=====================================
@@ -1427,7 +1427,7 @@ see Note [Data-con worker strictness].
--
-- [n] nontermination (e.g. loops)
-- [i] throws imprecise exception
--- [p] throws precise exceTtion
+-- [p] throws precise exception
-- [c] converges (reduces to WHNF).
--
-- The different lattice elements correspond to different subsets, indicated by
=====================================
compiler/Setup.hs
=====================================
@@ -38,12 +38,13 @@ primopIncls =
[ ("primop-data-decl.hs-incl" , "--data-decl")
, ("primop-tag.hs-incl" , "--primop-tag")
, ("primop-list.hs-incl" , "--primop-list")
- , ("primop-has-side-effects.hs-incl" , "--has-side-effects")
+ , ("primop-effects.hs-incl" , "--primop-effects")
, ("primop-out-of-line.hs-incl" , "--out-of-line")
, ("primop-commutable.hs-incl" , "--commutable")
, ("primop-code-size.hs-incl" , "--code-size")
- , ("primop-can-fail.hs-incl" , "--can-fail")
, ("primop-strictness.hs-incl" , "--strictness")
+ , ("primop-is-work-free.hs-incl" , "--is-work-free")
+ , ("primop-is-cheap.hs-incl" , "--is-cheap")
, ("primop-fixity.hs-incl" , "--fixity")
, ("primop-primop-info.hs-incl" , "--primop-primop-info")
, ("primop-vector-uniques.hs-incl" , "--primop-vector-uniques")
=====================================
hadrian/src/Rules/Generate.hs
=====================================
@@ -82,16 +82,17 @@ compilerDependencies = do
stage <- getStage
ghcPath <- expr $ buildPath (vanillaContext stage compiler)
pure $ (ghcPath -/-) <$>
- [ "primop-can-fail.hs-incl"
- , "primop-code-size.hs-incl"
+ [ "primop-code-size.hs-incl"
, "primop-commutable.hs-incl"
, "primop-data-decl.hs-incl"
, "primop-fixity.hs-incl"
- , "primop-has-side-effects.hs-incl"
+ , "primop-effects.hs-incl"
, "primop-list.hs-incl"
, "primop-out-of-line.hs-incl"
, "primop-primop-info.hs-incl"
, "primop-strictness.hs-incl"
+ , "primop-is-work-free.hs-incl"
+ , "primop-is-cheap.hs-incl"
, "primop-tag.hs-incl"
, "primop-vector-tycons.hs-incl"
, "primop-vector-tys-exports.hs-incl"
=====================================
hadrian/src/Rules/Lint.hs
=====================================
@@ -98,11 +98,12 @@ hsIncls path = [ path </> "primop-vector-tycons.hs-incl"
, path </> "primop-tag.hs-incl"
, path </> "primop-list.hs-incl"
, path </> "primop-strictness.hs-incl"
+ , path </> "primop-is-work-free.hs-incl"
+ , path </> "primop-is-cheap.hs-incl"
, path </> "primop-fixity.hs-incl"
, path </> "primop-docs.hs-incl"
, path </> "primop-primop-info.hs-incl"
, path </> "primop-out-of-line.hs-incl"
- , path </> "primop-has-side-effects.hs-incl"
- , path </> "primop-can-fail.hs-incl"
+ , path </> "primop-effects.hs-incl"
, path </> "primop-commutable.hs-incl"
]
=====================================
hadrian/src/Settings/Builders/GenPrimopCode.hs
=====================================
@@ -9,12 +9,13 @@ genPrimopCodeBuilderArgs = builder GenPrimopCode ? mconcat
, output "//primop-data-decl.hs-incl" ? arg "--data-decl"
, output "//primop-tag.hs-incl" ? arg "--primop-tag"
, output "//primop-list.hs-incl" ? arg "--primop-list"
- , output "//primop-has-side-effects.hs-incl" ? arg "--has-side-effects"
+ , output "//primop-effects.hs-incl" ? arg "--primop-effects"
, output "//primop-out-of-line.hs-incl" ? arg "--out-of-line"
, output "//primop-commutable.hs-incl" ? arg "--commutable"
, output "//primop-code-size.hs-incl" ? arg "--code-size"
- , output "//primop-can-fail.hs-incl" ? arg "--can-fail"
, output "//primop-strictness.hs-incl" ? arg "--strictness"
+ , output "//primop-is-work-free.hs-incl" ? arg "--is-work-free"
+ , output "//primop-is-cheap.hs-incl" ? arg "--is-cheap"
, output "//primop-fixity.hs-incl" ? arg "--fixity"
, output "//primop-primop-info.hs-incl" ? arg "--primop-primop-info"
, output "//primop-vector-uniques.hs-incl" ? arg "--primop-vector-uniques"
=====================================
libraries/base/GHC/Clock.hsc
=====================================
@@ -1,3 +1,4 @@
+{-# LANGUAGE CPP #-}
{-# LANGUAGE Trustworthy #-}
{-# LANGUAGE NoImplicitPrelude #-}
@@ -9,17 +10,36 @@ module GHC.Clock
import GHC.Base
import GHC.Real
import Data.Word
+#if defined(javascript_HOST_ARCH)
+import GHC.Num
+#endif
-- | Return monotonic time in seconds, since some unspecified starting point
--
-- @since 4.11.0.0
getMonotonicTime :: IO Double
-getMonotonicTime = do w <- getMonotonicTimeNSec
- return (fromIntegral w / 1000000000)
+getMonotonicTime = do
+#if defined(javascript_HOST_ARCH)
+ w <- getMonotonicTimeMSec
+ return (w / 1000)
+#else
+ w <- getMonotonicTimeNSec
+ return (fromIntegral w / 1000000000)
+#endif
-- | Return monotonic time in nanoseconds, since some unspecified starting point
--
-- @since 4.11.0.0
+#if defined(javascript_HOST_ARCH)
+getMonotonicTimeNSec :: IO Word64
+getMonotonicTimeNSec = do
+ w <- getMonotonicTimeMSec
+ return (floor w * 1000000)
+
+foreign import javascript unsafe "performance.now" getMonotonicTimeMSec:: IO Double
+
+
+#else
foreign import ccall unsafe "getMonotonicNSec"
getMonotonicTimeNSec :: IO Word64
-
+#endif
=====================================
libraries/base/GHC/Conc/POSIX.hs
=====================================
@@ -49,6 +49,7 @@ module GHC.Conc.POSIX
import Data.Bits (shiftR)
import GHC.Base
+import GHC.Clock
import GHC.Conc.Sync
import GHC.Conc.POSIX.Const
import GHC.Event.Windows.ConsoleEvent
@@ -209,13 +210,9 @@ delayTime (Delay t _) = t
delayTime (DelaySTM t _) = t
type USecs = Word64
-type NSecs = Word64
-
-foreign import ccall unsafe "getMonotonicNSec"
- getMonotonicNSec :: IO NSecs
getMonotonicUSec :: IO USecs
-getMonotonicUSec = fmap (`div` 1000) getMonotonicNSec
+getMonotonicUSec = fmap (`div` 1000) getMonotonicTimeNSec
{-# NOINLINE prodding #-}
prodding :: IORef Bool
=====================================
libraries/base/GHC/IO/Exception.hs
=====================================
@@ -329,13 +329,16 @@ type IOError = IOException
-- flagged.
data IOException
= IOError {
- ioe_handle :: Maybe Handle, -- the handle used by the action flagging
- -- the error.
- ioe_type :: IOErrorType, -- what it was.
- ioe_location :: String, -- location.
- ioe_description :: String, -- error type specific information.
- ioe_errno :: Maybe CInt, -- errno leading to this error, if any.
- ioe_filename :: Maybe FilePath -- filename the error is related to.
+ ioe_handle :: Maybe Handle, -- ^ the handle used by the action flagging
+ -- the error.
+ ioe_type :: IOErrorType, -- ^ what it was.
+ ioe_location :: String, -- ^ location.
+ ioe_description :: String, -- ^ error type specific information.
+ ioe_errno :: Maybe CInt, -- ^ errno leading to this error, if any.
+ ioe_filename :: Maybe FilePath -- ^ filename the error is related to
+ -- (some libraries may assume different encodings
+ -- when constructing this field from e.g. 'ByteString'
+ -- or other types)
}
-- | @since 4.1.0.0
=====================================
libraries/base/Unsafe/Coerce.hs
=====================================
@@ -78,9 +78,11 @@ floating.
But what stops the whole (case unsafeEqualityProof of ...) from
floating? Answer: we never float a case on a redex that can fail
outside a conditional. See Primop.hs,
-Note [Transformations affected by can_fail and has_side_effects].
+Note [Transformations affected by primop effects].
And unsafeEqualityProof (being opaque) is definitely treated as
can-fail.
+ (Huh? It seems we have a special-case in exprOkForSpeculation (app_ok)
+ /specifically/ allowing unsafeEqualityProof. Something smells wrong.)
While unsafeCoerce is a perfectly ordinary function that needs no
special treatment, Unsafe.Coerce.unsafeEqualityProof is magical, in
=====================================
libraries/base/tests/T23687.hs
=====================================
@@ -0,0 +1,14 @@
+module Main where
+
+import GHC.Clock
+import Control.Monad
+
+main :: IO ()
+main = do
+ a <- getMonotonicTimeNSec
+ b <- getMonotonicTimeNSec
+ when (a > b) $ putStrLn "Non-monotonic time"
+
+ c <- getMonotonicTime
+ d <- getMonotonicTime
+ when (c > d) $ putStrLn "Non-monotonic time"
=====================================
libraries/base/tests/all.T
=====================================
@@ -310,3 +310,4 @@ test('inits1tails1', normal, compile_and_run, [''])
test('CLC149', normal, compile, [''])
test('AtomicSwapIORef', normal, compile_and_run, [''])
test('T23454', normal, compile_fail, [''])
+test('T23687', normal, compile_and_run, [''])
=====================================
testsuite/tests/ghc-api/PrimOpEffect_Sanity.hs
=====================================
@@ -0,0 +1,17 @@
+import GHC.Builtin.PrimOps
+import GHC.Utils.Outputable
+
+main :: IO ()
+main = let
+ errs = do
+ op <- allThePrimOps
+ case primOpEffect op of
+ NoEffect -> []
+ CanFail -> []
+ ThrowsException -> []
+ ReadWriteEffect
+ -> [ppr op <+> text "has ReadWriteEffect but is work-free"
+ | primOpIsWorkFree op]
+ ++ [ppr op <+> text "has ReadWriteEffect but is cheap"
+ | primOpIsCheap op]
+ in putStrLn $ showSDocUnsafe (vcat errs)
=====================================
testsuite/tests/ghc-api/all.T
=====================================
@@ -37,3 +37,4 @@ test('T19156', [ extra_run_opts('"' + config.libdir + '"')
test('T20757', [unless(opsys('mingw32'), skip), exit_code(1)],
compile_and_run,
['-package ghc'])
+test('PrimOpEffect_Sanity', normal, compile_and_run, ['-Wall -Werror -package ghc'])
=====================================
utils/genprimopcode/AccessOps.hs
=====================================
@@ -83,7 +83,7 @@ mkIndexByteArrayOp e = PrimOpSpec
(elt_rep_ty e)
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ "."
- , opts = [OptionTrue "can_fail"]
+ , opts = [OptionEffect CanFail]
}
mkUnalignedIndexByteArrayOp :: ElementType -> Entry
@@ -95,7 +95,7 @@ mkUnalignedIndexByteArrayOp e = PrimOpSpec
(elt_rep_ty e)
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail"]
+ , opts = [OptionEffect CanFail]
}
mkReadByteArrayOp :: ElementType -> Entry
@@ -107,7 +107,7 @@ mkReadByteArrayOp e = PrimOpSpec
$ readResTy e
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ "."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
mkUnalignedReadByteArrayOp :: ElementType -> Entry
@@ -119,7 +119,7 @@ mkUnalignedReadByteArrayOp e = PrimOpSpec
$ readResTy e
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
mkWriteByteArrayOp :: ElementType -> Entry
@@ -131,7 +131,7 @@ mkWriteByteArrayOp e = PrimOpSpec
$ writeResTy e
, cat = GenPrimOp
, desc = "Write " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ "."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
mkUnalignedWriteByteArrayOp :: ElementType -> Entry
@@ -143,7 +143,7 @@ mkUnalignedWriteByteArrayOp e = PrimOpSpec
$ writeResTy e
, cat = GenPrimOp
, desc = "Write " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
@@ -168,7 +168,7 @@ mkIndexOffAddrOp e = PrimOpSpec
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ ".\n\n"
++ getAlignWarn e
- , opts = [OptionTrue "can_fail"]
+ , opts = [OptionEffect CanFail]
}
{-
@@ -181,7 +181,7 @@ mkUnalignedIndexOffAddrOp e = PrimOpSpec
(elt_rep_ty e)
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail"]
+ , opts = [OptionEffect CanFail]
}
-}
@@ -195,7 +195,7 @@ mkReadOffAddrOp e = PrimOpSpec
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ ".\n\n"
++ getAlignWarn e
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
{-
@@ -208,7 +208,7 @@ mkUnalignedReadOffAddrOp e = PrimOpSpec
$ readResTy e
, cat = GenPrimOp
, desc = "Read " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
-}
@@ -222,7 +222,7 @@ mkWriteOffAddrOp e = PrimOpSpec
, cat = GenPrimOp
, desc = "Write " ++ elt_desc e ++ "; offset in " ++ prettyOffset e ++ ".\n\n"
++ getAlignWarn e
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
{-
@@ -235,7 +235,7 @@ mkUnalignedWriteOffAddrOp e = PrimOpSpec
$ writeResTy e
, cat = GenPrimOp
, desc = "Write " ++ elt_desc e ++ "; offset in bytes."
- , opts = [OptionTrue "can_fail", OptionTrue "has_side_effects"]
+ , opts = [OptionEffect ReadWriteEffect, OptionCanFailWarnFlag YesWarnCanFail]
}
-}
=====================================
utils/genprimopcode/Lexer.x
=====================================
@@ -51,6 +51,15 @@ words :-
<0> "infixl" { mkT TInfixL }
<0> "infixr" { mkT TInfixR }
<0> "Nothing" { mkT TNothing }
+ <0> "effect" { mkT TEffect }
+ <0> "NoEffect" { mkT TNoEffect }
+ <0> "CanFail" { mkT TCanFail }
+ <0> "ThrowsException" { mkT TThrowsException }
+ <0> "ReadWriteEffect" { mkT TReadWriteEffect }
+ <0> "can_fail_warning" { mkT TCanFailWarnFlag }
+ <0> "DoNotWarnCanFail" { mkT TDoNotWarnCanFail }
+ <0> "WarnIfEffectIsCanFail" { mkT TWarnIfEffectIsCanFail }
+ <0> "YesWarnCanFail" { mkT TYesWarnCanFail }
<0> "vector" { mkT TVector }
<0> "bytearray_access_ops" { mkT TByteArrayAccessOps }
<0> "addr_access_ops" { mkT TAddrAccessOps }
=====================================
utils/genprimopcode/Main.hs
=====================================
@@ -126,11 +126,6 @@ main = getArgs >>= \args ->
"--data-decl"
-> putStr (gen_data_decl p_o_specs)
- "--has-side-effects"
- -> putStr (gen_switch_from_attribs
- "has_side_effects"
- "primOpHasSideEffects" p_o_specs)
-
"--out-of-line"
-> putStr (gen_switch_from_attribs
"out_of_line"
@@ -146,10 +141,15 @@ main = getArgs >>= \args ->
"code_size"
"primOpCodeSize" p_o_specs)
- "--can-fail"
+ "--is-work-free"
+ -> putStr (gen_switch_from_attribs
+ "work_free"
+ "primOpIsWorkFree" p_o_specs)
+
+ "--is-cheap"
-> putStr (gen_switch_from_attribs
- "can_fail"
- "primOpCanFail" p_o_specs)
+ "cheap"
+ "primOpIsCheap" p_o_specs)
"--strictness"
-> putStr (gen_switch_from_attribs
@@ -161,6 +161,11 @@ main = getArgs >>= \args ->
"fixity"
"primOpFixity" p_o_specs)
+ "--primop-effects"
+ -> putStr (gen_switch_from_attribs
+ "effect"
+ "primOpEffect" p_o_specs)
+
"--primop-primop-info"
-> putStr (gen_primop_info p_o_specs)
@@ -197,13 +202,14 @@ main = getArgs >>= \args ->
known_args :: [String]
known_args
= [ "--data-decl",
- "--has-side-effects",
"--out-of-line",
"--commutable",
"--code-size",
- "--can-fail",
+ "--is-work-free",
+ "--is-cheap",
"--strictness",
"--fixity",
+ "--primop-effects",
"--primop-primop-info",
"--primop-tag",
"--primop-list",
@@ -287,7 +293,9 @@ gen_hs_source (Info defaults entries) =
opt (OptionString n v) = n ++ " = { " ++ v ++ "}"
opt (OptionInteger n v) = n ++ " = " ++ show v
opt (OptionVector _) = ""
- opt (OptionFixity mf) = "fixity" ++ " = " ++ show mf
+ opt (OptionFixity mf) = "fixity = " ++ show mf
+ opt (OptionEffect eff) = "effect = " ++ show eff
+ opt (OptionCanFailWarnFlag wf) = "can_fail_warning = " ++ show wf
hdr s@(Section {}) = sec s
hdr (PrimOpSpec { name = n }) = wrapOp n ++ ","
@@ -341,7 +349,10 @@ gen_hs_source (Info defaults entries) =
can_fail options
= [ "can fail with an unchecked exception"
- | Just (OptionTrue _) <- [lookup_attrib "can_fail" options] ]
+ | Just (OptionEffect eff) <- [lookup_attrib "effect" options]
+ , Just (OptionCanFailWarnFlag wflag) <- [lookup_attrib "can_fail_warning" options]
+ , wflag /= DoNotWarnCanFail
+ , wflag == YesWarnCanFail || eff == CanFail ]
prim_deprecated options n
= [ "{-# DEPRECATED " ++ wrapOp n ++ " \"" ++ msg ++ "\" #-}"
@@ -608,6 +619,8 @@ gen_switch_from_attribs attrib_name fn_name (Info defaults entries)
getAltRhs (OptionString _ s) = s
getAltRhs (OptionVector _) = "True"
getAltRhs (OptionFixity mf) = show mf
+ getAltRhs (OptionEffect eff) = show eff
+ getAltRhs (OptionCanFailWarnFlag wf) = show wf
mkAlt po
= case lookup_attrib attrib_name (opts po) of
@@ -621,13 +634,13 @@ gen_switch_from_attribs attrib_name fn_name (Info defaults entries)
Nothing -> error ("gen_switch_from: " ++ attrib_name)
Just xx
-> unlines alternatives
- ++ fn_name ++ " _ = " ++ getAltRhs xx ++ "\n"
+ ++ fn_name ++ " _thisOp = " ++ getAltRhs xx ++ "\n"
{-
Note [GHC.Prim Docs]
~~~~~~~~~~~~~~~~~~~~
For haddocks of GHC.Prim we generate a dummy haskell file (gen_hs_source) that
-contains the type signatures and the commends (but no implementations)
+contains the type signatures and the comments (but no implementations)
specifically for consumption by haddock.
GHCi's :doc command reads directly from ModIface's though, and GHC.Prim has a
=====================================
utils/genprimopcode/Parser.y
=====================================
@@ -45,6 +45,15 @@ import AccessOps
infixl { TInfixL }
infixr { TInfixR }
nothing { TNothing }
+ effect { TEffect }
+ NoEffect { TNoEffect }
+ CanFail { TCanFail }
+ ThrowsException { TThrowsException }
+ ReadWriteEffect { TReadWriteEffect }
+ can_fail_warning { TCanFailWarnFlag }
+ DoNotWarnCanFail { TDoNotWarnCanFail }
+ WarnIfEffectIsCanFail { TWarnIfEffectIsCanFail }
+ YesWarnCanFail { TYesWarnCanFail }
vector { TVector }
SCALAR { TSCALAR }
VECTOR { TVECTOR }
@@ -77,6 +86,8 @@ pOption : lowerName '=' false { OptionFalse $1 }
| lowerName '=' integer { OptionInteger $1 $3 }
| vector '=' pVectorTemplate { OptionVector $3 }
| fixity '=' pInfix { OptionFixity $3 }
+ | effect '=' pEffect { OptionEffect $3 }
+ | can_fail_warning '=' pPrimOpCanFailWarnFlag { OptionCanFailWarnFlag $3 }
pInfix :: { Maybe Fixity }
pInfix : infix integer { Just $ Fixity NoSourceText $2 InfixN }
@@ -84,6 +95,16 @@ pInfix : infix integer { Just $ Fixity NoSourceText $2 InfixN }
| infixr integer { Just $ Fixity NoSourceText $2 InfixR }
| nothing { Nothing }
+pEffect :: { PrimOpEffect }
+pEffect : NoEffect { NoEffect }
+ | CanFail { CanFail }
+ | ThrowsException { ThrowsException }
+ | ReadWriteEffect { ReadWriteEffect }
+
+pPrimOpCanFailWarnFlag :: { PrimOpCanFailWarnFlag }
+pPrimOpCanFailWarnFlag : DoNotWarnCanFail { DoNotWarnCanFail }
+ | WarnIfEffectIsCanFail { WarnIfEffectIsCanFail }
+ | YesWarnCanFail { YesWarnCanFail }
pEntries :: { [Entry] }
pEntries : pEntry pEntries { $1 : $2 }
=====================================
utils/genprimopcode/ParserM.hs
=====================================
@@ -111,6 +111,15 @@ data Token = TEOF
| TInfixL
| TInfixR
| TNothing
+ | TEffect
+ | TNoEffect
+ | TCanFail
+ | TThrowsException
+ | TReadWriteEffect
+ | TCanFailWarnFlag
+ | TDoNotWarnCanFail
+ | TWarnIfEffectIsCanFail
+ | TYesWarnCanFail
| TVector
| TSCALAR
| TVECTOR
=====================================
utils/genprimopcode/Syntax.hs
=====================================
@@ -61,6 +61,8 @@ data Option
| OptionInteger String Int -- name = <int>
| OptionVector [(String,String,Int)] -- name = [(,...),...]
| OptionFixity (Maybe Fixity) -- fixity = infix{,l,r} <int> | Nothing
+ | OptionEffect PrimOpEffect -- effect = NoEffect | DoNotSpeculate | CanFail | ThrowsException | ReadWriteEffect | FallibleReadWriteEffect
+ | OptionCanFailWarnFlag PrimOpCanFailWarnFlag -- can_fail_warning = DoNotWarnCanFail | WarnIfEffectIsCanFail | YesWarnCanFail
deriving Show
-- categorises primops
@@ -109,6 +111,19 @@ data SourceText = SourceText String
| NoSourceText
deriving (Eq,Show)
+data PrimOpEffect
+ = NoEffect
+ | CanFail
+ | ThrowsException
+ | ReadWriteEffect
+ deriving (Eq, Show)
+
+data PrimOpCanFailWarnFlag
+ = DoNotWarnCanFail
+ | WarnIfEffectIsCanFail
+ | YesWarnCanFail
+ deriving (Eq, Show)
+
------------------------------------------------------------------
-- Sanity checking -----------------------------------------------
------------------------------------------------------------------
@@ -131,7 +146,7 @@ sanityTop :: Info -> ()
sanityTop (Info defs entries)
= let opt_names = map get_attrib_name defs
primops = filter is_primop entries
- in
+ in
if length opt_names /= length (nub opt_names)
then error ("non-unique default attribute names: " ++ show opt_names ++ "\n")
else myseqAll (map (sanityPrimOp opt_names) primops) ()
@@ -168,6 +183,8 @@ get_attrib_name (OptionString nm _) = nm
get_attrib_name (OptionInteger nm _) = nm
get_attrib_name (OptionVector _) = "vector"
get_attrib_name (OptionFixity _) = "fixity"
+get_attrib_name (OptionEffect _) = "effect"
+get_attrib_name (OptionCanFailWarnFlag _) = "can_fail_warning"
lookup_attrib :: String -> [Option] -> Maybe Option
lookup_attrib _ [] = Nothing
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/59c6d66624f1cafd8d6d80de6c7bfc8bf15ab46c...2810170db7d66e385adb9199c85a306a8bf1cf56
--
View it on GitLab: https://gitlab.haskell.org/ghc/ghc/-/compare/59c6d66624f1cafd8d6d80de6c7bfc8bf15ab46c...2810170db7d66e385adb9199c85a306a8bf1cf56
You're receiving this email because of your account on gitlab.haskell.org.
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.haskell.org/pipermail/ghc-commits/attachments/20230731/c25f8e0f/attachment-0001.html>
More information about the ghc-commits
mailing list