... | ... | @@ -43,46 +43,7 @@ This pragma can only be used for bindings in the current module (exactly like an |
|
|
## The VECTORISE SCALAR pragma for functions
|
|
|
|
|
|
|
|
|
Functions that contain no array computations, especially if they are cheap (such as `(+)`), should not be vectorised, but applied by simply mapping them over an array. This could be achieved by using the `VECTORISE` pragma with an appropriate right-hand side, but leads to repetitive code that we rather like the compiler to generate.
|
|
|
|
|
|
|
|
|
If a unary function `f` is accompanied by a pragma
|
|
|
|
|
|
```wiki
|
|
|
{-# VECTORISE SCALAR f #-}
|
|
|
```
|
|
|
|
|
|
|
|
|
then the vectoriser generates
|
|
|
|
|
|
```wiki
|
|
|
f_v = closure1 f (scalar_map f)
|
|
|
```
|
|
|
|
|
|
|
|
|
and keeps `f` unchanged.
|
|
|
|
|
|
|
|
|
For a binary function, it generates
|
|
|
|
|
|
```wiki
|
|
|
f_v = closure2 f (scalar_zipWith f)
|
|
|
```
|
|
|
|
|
|
|
|
|
for a tertiary function, it generates
|
|
|
|
|
|
```wiki
|
|
|
f_v = closure3 f (scalar_zipWith3 f)
|
|
|
```
|
|
|
|
|
|
|
|
|
and so on. (The variable `f` must have a proper function type.)
|
|
|
|
|
|
|
|
|
This pragma can also be used on imported functions `f`, in the same manner as the plain `VECTORISE` pragma.
|
|
|
|
|
|
**RESTRICTION:** Functions vectorised with this pragma can not be mutually recursive. (They may be recursive.)
|
|
|
Removed.
|
|
|
|
|
|
## The basic VECTORISE pragma for type constructors
|
|
|
|
... | ... | @@ -99,10 +60,10 @@ For a type constructor `T`, the pragma |
|
|
indicates that the type `T` should be automatically vectorised even if it is imported. This is the default for all data types declared in the current module. If the type embeds no parallel arrays, no special vectorised representation will be generated.
|
|
|
|
|
|
|
|
|
The type constructor `T` must be in scope, but it may be imported. 'PData' and 'PRepr' instances are automatically generated by the vectoriser.
|
|
|
The type constructor `T` must be in scope, but it may be imported. `PData` and `PRepr` instances are automatically generated by the vectoriser.
|
|
|
|
|
|
|
|
|
Examples are the vectorisation of types, such as `Ordering` and `[]`, defined in the `Prelude`.
|
|
|
Examples are the vectorisation of types, such as `Maybe` and `[]`, defined in the `Prelude`.
|
|
|
|
|
|
### With right-hand side
|
|
|
|
... | ... | @@ -114,16 +75,19 @@ For a type constructor `T`, the pragma |
|
|
```
|
|
|
|
|
|
|
|
|
directs the vectoriser to replace `T` by `T'` in vectorised code and to use the corresponding data constructors of `T'` instead of those of `T`. This implies that the two type constructors must be of the same kind and that the data constructors of `T` and `T'` need to be of the same number and type.
|
|
|
directs the vectoriser to replace `T` by `T'` in vectorised code. Vectorisation of `T` is abstract in that constructors of `T` may not occur in vectorised code.
|
|
|
|
|
|
|
|
|
The type constructor `T` must be in scope, but it may be imported. 'PData' and 'PRepr' instances must be explicitly defined — they are not automatically generated.
|
|
|
The type constructor `T` must be in scope, but it may be imported. `PData` and `PRepr` instances must be explicitly defined — they are not automatically generated.
|
|
|
|
|
|
|
|
|
An example is the handling of `Bool`, where we represent `Bool` by itself in vectorised code, but want to use the custom instances of 'PData' and 'PRepr' defined in the DPH libraries.
|
|
|
An example is the vectorisation of parallel arrays, where `[::]` is replaced by `PArray` during vectorisation, but the vectoriser never looks at the representation of `[::]`.
|
|
|
|
|
|
## The VECTORISE SCALAR pragma for type constructors
|
|
|
|
|
|
|
|
|
All types imported from modules that have not been vectorised are regarded to be scalar types, and they can be used in encapsulated scalar code. If custom instances for the `PData` and `PRepr` classes are provided, these types can also be used in vectorisation code. The types represent themselves (as they are scalar) and are abstract; i.e., their constructors cannot be used in vectorised code. An example is the treatment of `Int`. `Int`s can be used in vectorised code and remain unchanged by vectorisation. However, the representation of `Int` by the `I#` data constructor wrapping an `Int#` is not exposed in vectorised code. Instead, computations involving the representation need to be confined to scalar code.
|
|
|
|
|
|
### Without right-hand side
|
|
|
|
|
|
|
... | ... | @@ -134,20 +98,13 @@ For a type constructor `T`, the pragma |
|
|
```
|
|
|
|
|
|
|
|
|
indicates that the type is scalar; i.e., it has no embedded arrays and its constructors can **only** be used in scalar code. Note that the type cannot be parameterised (as we would not be able to rule out that a type parameter is instantiated with an array type at a usage site.)
|
|
|
|
|
|
indicates that the type `T` is scalar; i.e., it cannot have any embedded arrays. Hence, the type `T` represents itself in vectorised code. (No special vectorised version needs to be generated.)
|
|
|
|
|
|
Due to this pragma declaration, `T` that may be used in vectorised code, where `T` represents itself. However, the representation of `T` is opaque in vectorised code. An example is the treatment of `Int`. `Int`s can be used in vectorised code and remain unchanged by vectorisation. However, the representation of `Int` by the `I#` data constructor wrapping an `Int#` is not exposed in vectorised code. Instead, computations involving the representation need to be confined to scalar code.
|
|
|
|
|
|
The type constructor `T` must be in scope, but it may be imported. `PData` and `PRepr` instances for `T` need to be manually defined if needed at all. (This is the fundamental difference to types for which the vectoriser determines automatically that they don't need a vectorised version: for the latter, the vectoriser automatically generates instances for `PData` and `PRepr`.)
|
|
|
|
|
|
The type constructor `T` must be in scope, but it may be imported. The `PData` and `PRepr` instances for `T` need to be manually defined. (For types that the vectoriser automatically determines that they don't need a vectorised version, instances for `PData` and `PRepr` are still generated automatically.)
|
|
|
|
|
|
|
|
|
NB: The crucial difference between `{-# VECTORISE SCALAR type T1 #-}` and `{-# VECTORISE type T2 #-}`, where `T2` embeds no parallel arrays, is that the \*representation\* (i.e., the constructors) of the former can only be used in scalar code. However, neither the representation of `T1` nor `T2` does get vectorised — so, both types are suitable for code that does not get vectorised due to vectorisation avoidance.
|
|
|
|
|
|
**TODO**
|
|
|
|
|
|
- For type constructors identified with this pragma, can we generate an `instance` of the `Scalar` type class automatically (instead of relying on it being in the library)?
|
|
|
An example is the handling of `Bool`, which is scalar and represents itself in vectorised code, but we want to use the custom instances of 'PData' and 'PRepr' defined in the DPH libraries.
|
|
|
|
|
|
### With right-hand side
|
|
|
|
... | ... | @@ -159,17 +116,32 @@ For a type constructor `T`, the pragma |
|
|
```
|
|
|
|
|
|
|
|
|
directs the vectoriser to replace `T` by `T'` in vectorised code, but its constructors can **only** be used in scalar code.
|
|
|
directs the vectoriser to replace `T` by `T'` in vectorised code, but the type is abstract — i.e., its constructors cannot be used in vectorised code. Although, the representation of the type changes during vectorisation, it is still regarded as scalar, and hence, can be used in encapsulated scalar code.
|
|
|
|
|
|
|
|
|
The type constructor `T` must be in scope, but it may be imported. The `PData` and `PRepr` instances for `T` need to be manually defined.
|
|
|
|
|
|
|
|
|
An example is the handling of `[::]`, which the vectoriser maps to `PArray`, but it never looks at the implementation of `[::]`.
|
|
|
An example is the handling of `(->)`, which the vectoriser maps to `(:->)`, but it never looks at the implementation of `(->)` and allows its use in encapsulated scalar code.
|
|
|
|
|
|
## The NOVECTORISE pragma for types
|
|
|
|
|
|
|
|
|
If a type constructor `T` is accompanied by a pragma
|
|
|
|
|
|
```wiki
|
|
|
{-# NOVECTORISE type T #-}
|
|
|
```
|
|
|
|
|
|
|
|
|
then it is ignored by the vectoriser — i.e., no type `T_v` and no class instances are generated.
|
|
|
|
|
|
|
|
|
This pragma can only be used for definitions in the current module.
|
|
|
|
|
|
**TODO**
|
|
|
|
|
|
- Maybe `VECTORISE ABSTRACT` would be a better name as it doesn't guarantee that the type constructor can be used in scalar code that doesn't need to be vectorised. It just means that the data constructors can **only** be used in scalar code — i.e., the vectoriser treats it as an abstract type.
|
|
|
- Not implemented yet.
|
|
|
|
|
|
## The VECTORISE pragma for type classes
|
|
|
|
... | ... | @@ -212,6 +184,10 @@ An example is `{-# VECTORISE SCALAR instance Num Int #-}`. |
|
|
|
|
|
**RESTRICTION:** Instance dictionaries vectorised with this pragma can not be mutually recursive. (They may be recursive.)
|
|
|
|
|
|
**TODO**
|
|
|
|
|
|
- This pragma should now be **redundant**, as uses of unvectorised class instances should just be encapsulated. (Do we still need the `VECTRORISE class` pragma? Probably only if we want to allow custom instances in vectorised modules.)
|
|
|
|
|
|
## Vectorising imported definitions
|
|
|
|
|
|
|
... | ... | |