WIP: Virtual data constructors
Seems to mostly work now and we don't allocate virtual constructors but doesn't quite produce optimal code yet.
This MR allows us to safely solve the problem described in #21617 as well as optimizing certain code. The gist of it is this:
For a data type with a single constructor and a singular strict field (either banged or unlifted) where the runtime representation of the field is compatible with lifted-boxed-rep we can omit allocating the constructor at runtime and instead pass around the pointer of the field itself to represent the value. This MR calls the constructor of such a type a virtual constructor as it never actually exists at runtime.
From the original ticket #21617 if we have:
type U :: UnliftedType
data U = U1 | U2 <params> | ...
data L = L U
We never need to allocate the constructor L
. Why not?
- If we have
x = L u
we just return a pointer tou
instead of allocating the constructor. - If we have
case x of L u -> u
when generating code to bind the variableu
instead of indexing into the closure forx
we simply return the result of evaluatingx
.
We can apply this to a few more variations of data types as a nice side effect.
Here are a few example types where we can treat their constructor as a virtual constructor:
-- Single strict lifted polymorphic field.
data Indirection a = Ind !a
-- Functions in strict fields.
data IndFun a b = IndFun { unIndFun :: !(a -> b) }
data IndLBool = IndB { unIndB :: !Bool }
data IndIIFunc = IndIIFunc { unFunii :: !(Int -> Int) }
-- Unlifted fields where the type is known.
data LUBool = LB { unBool :: UBool }
Where this, perhaps surprisingly, doesn't work is this type:
data UIndirection (a :: UnliftedType) = UInd { u_unInd :: a }
That's because it could be instantiated with a ByteArray# or similar type which has no entry code.
So code like this:
let x = UInd arr
in seq x ()
would blow up as after the optimization we would try to seq the arr
value.