Note [Absent fillers]
Consider
data T = MkT [Int] [Int] ![Int] -- NB: last field is strict
f :: T -> Int# -> blah
f ps w = case ps of MkT xs ys zs -> <body mentioning xs>
Then f gets a strictness sig of <S(L,A,A)><A>. We make a worker $wf thus:
$wf :: [Int] -> blah
$wf xs = case ps of MkT xs _ _ -> <body mentioning xs>
where
ys = absentError "ys :: [Int]"
zs = RUBBISH[LiftedRep] @[Int]
ps = MkT xs ys zs
w = RUBBISH[IntRep] @Int#
The absent arguments 'ys', 'zs' and 'w' aren't even passed to the worker.
And neither should they! They are never used, their value is irrelevant (hence
they are *dead code*) and they are probably discarded after the next run of the
Simplifier (when they are in fact *unreachable code*). Yet, we have to come up
with "filler" values that we bind the absent arg Ids to.
That is exactly what Note [Rubbish literals] are for: A convenient way to
conjure filler values at any type (and any representation or levity!).
Needless to say, there are some wrinkles:
(AF1) In case we have a absent, /lazy/, and /lifted/ arg, we use an error-thunk
instead. If absence analysis was wrong (e.g., #11126) and the binding
in fact is used, then we get a nice panic message instead of undefined
runtime behavior (See Modes of failure from Note [Rubbish literals]).
Obviously, we can't use an error-thunk if the value is of unlifted rep
(like 'Int#' or 'MutVar#'), because we'd immediately evaluate the panic.
(AF2) We also mustn't put an error-thunk (that fills in for an absent value of
lifted rep) in a strict field, because #16970 establishes the invariant
that strict fields are always evaluated, by possibly (re-)evaluating what is put in
a strict field. That's the reason why 'zs' binds a rubbish literal instead
of an error-thunk, see #19133.
How do we detect when we are about to put an error-thunk in a strict field?
Ideally, we'd just look at the 'StrictnessMark' of the DataCon's field. So that's
what we do!
There are other necessary conditions for strict fields:
Note [Unboxing evaluated arguments] in DmdAnal makes it so that the demand on
'zs' is absent and /strict/: It will get cardinality 'C_10', the empty
interval, rather than 'C_00'. Hence the 'isStrictDmd' check: It further
guarantees e never fill in an error-thunk for an absent strict field.
But that also means we emit a rubbish lit for other args that have
cardinality 'C_10' (say, the arg to a bottoming function) where we could've
used an error-thunk.
NB from Andreas: But I think using an error thunk there would be dodgy no matter what
for example if we decide to pass the argument to the bottoming function cbv.
As we might do if the function in question is a worker.
See Note [CBV Function Ids] in GHC.Types.Id.Info. So I just left the strictness check
in place on top of threading through the marks from the constructor. It's a *really* cheap
and easy check to make anyway.
(AF3) We can only emit a LitRubbish if the arg's type `arg_ty` is mono-rep, e.g.
of the form `TYPE rep` where `rep` is not (and doesn't contain) a variable.
Why? Because if we don't know its representation (e.g. size in memory,
register class), we don't know what or how much rubbish to emit in codegen.
'mkLitRubbish' returns 'Nothing' in this case and we simply fall
back to passing the original parameter to the worker.
Note that currently this case should not occur, because binders always
have to be representation monomorphic. But in the future, we might allow
levity polymorphism, e.g. a polymorphic levity variable in 'BoxedRep'.
(AF4) Consider (#24934)
f :: (a~b) => blah {-# INLINE f # References 3
- Unboxing evaluated arguments GHC.Core.Opt.DmdAnal
- CBV Function Ids GHC.Types.Id.Info
- Rubbish literals GHC.Types.Literal
Referenced by 10
- GHC.Core.Opt.WorkWrap.Utils call site ×5
- aBSENT_ERROR_ID GHC.Core.Make
- GHC.Core.Make call site
- Analysing with absent demand GHC.Core.Opt.DmdAnal
- Which Ids should be strictified GHC.Core.Utils
- Rubbish literals GHC.Types.Literal