Version 6 (modified by chak, 9 years ago) (diff) |
---|

## Type Vectorisation

The transformation of types includes both closure conversion and the pairing of scalar with lifted computations.

### Unboxed types

Unboxed types and functions defined in `GHC.Prim` need to be treated specially during vectorisation. This is as we cannot have `PA` instances for unboxed types and the transformation needs to know which functions from `GHC.Prim` can be safely parallelised (e.g., its fine to run many `+#` in parallel, whereas this is not really advisable for calls to side-effecting RTS functions). Indeed, we might regard unboxed types and functions from `GHC.Prim` as the place where we make the transition from implementing vectorisation decisions in package ndp to hard-coding them into the compiler. It is probably a good idea to eventually move as much as possible of the hardcoded information into `primops.txt.pp`, but for the moment, we simply hardcode everything in the modules in `vectorise/`.

To treat unboxed type properly, we cannot simply use the type constructor `PArr` wherever we need a flattened array; instead, we define a type translation

Int#^ = UArr Int Float#^ = UArr Float Double#^ = UArr Double <and so on for other unboxed types> t^ = PArr t*

We need to represent functions whose argument and/or result type are unboxed different from functions over boxed types. The reason is the non-standard kinding rule implemented in GHC for `(->)`, which allows that the two argument type variables are instantiated to unboxed values iff the application of `(->)` is saturated. We can't defined a second type constructor with that property unless we extend the `TypeRep.Type` representation. We also can't simply use a type synonym for a vectorised type function constructor, because we must be able to partially apply it.

### Transformation rules

TODO

- Be careful that
`VFun (t1* -> t2*)`and`t1* -> t2*`includes`PArr t1`and`PArr t2*`; so, we can only use them if we have`PA`instances for these types.

The type transformation rules achieve two goals: (1) they replace original type constructors and variables by their vectorised variants, where those are available, and (2) they alter the representation of functions:

T* = T_V , if T_V exists = T , otherwise a* = a_v (t1 -> t2)* | isUbxFun (t1->t2) = (t1* -> t2*) :|| (t1^ -> t2^) | otherwise = t1* :-> t2* (t1 t2)* = t1* t2* (forall a.t)* = forall a_v.t*

We need to distinguish between saturated function space applications involving unboxed types and those that don't, as we need to remain to be compatible with `(->_v) = (:->)`.

T* = T_V , if T_V exists = T , otherwise a* = a_v (t1 -> t2)* = ( t1* -> t2*, , if kindOf t1 == # [:t1* -> t2*:]) or kindOf t2 == # = ( t1* :-> t2*, , otherwise [:t1* :-> t2*:]) (t1 t2)* = t1* t2* (forall a.t)* = forall a_v.t*