fir-0.1.0.0: src/FIR/AST/Prim.hs
{-# OPTIONS_GHC -fno-warn-missing-pattern-synonym-signatures -Wno-missing-signatures #-}
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-|
Module: FIR.AST.Prim
Primitive operations of the AST:
* lambda abstraction,
* function application,
* constant literals;
* primitive SPIR-V operations,
* vector/array/matrix creation,
* ...
-}
module FIR.AST.Prim where
-- base
import Data.Foldable
( toList )
import Data.Kind
( Type )
import GHC.TypeLits
( Symbol )
import Data.Proxy
( Proxy )
import Data.Word
( Word32 )
import GHC.TypeLits
( KnownSymbol, symbolVal )
import GHC.TypeNats
( KnownNat, natVal )
import Numeric.Natural
( Natural )
import Data.Type.Map
-- containers
import Data.Tree
( Tree(Node) )
-- text-short
import Data.Text.Short
( ShortText )
-- variant
import Data.Variant.EGADT
( EGADT, pattern VF, (:<!) )
-- vector-sized
import qualified Data.Vector.Sized as Vector
( knownLength', toList )
-- fir
import CodeGen.Instruction
( ID )
import CodeGen.Monad
( MonadFresh(fresh) )
import Data.Constraint.All
( All )
import {-# SOURCE #-} Data.Product
( HList )
import Data.Type.Known
( knownValue )
import FIR.AST.Display
( Display(toTreeArgs), named )
import FIR.AST.Type
( AugType(Val, (:-->)), Eff
, FunArgs, FunRes
, ApplyFAug
)
import FIR.Prim.Array
( Array(MkArray) )
import FIR.Prim.Op
( PrimOp(PrimOpAugType, opName)
, PrimTyVal
)
import {-# SOURCE #-} FIR.Prim.Struct
( Struct, ASTStructFields, traverseStructASTs )
import {-# SOURCE #-} FIR.Prim.Types
( PrimTy, primTy
, PrimTyMap, primTyMap
, PrimFunc, primFuncName
)
import Math.Algebra.GradedSemigroup
( GradedSemigroup(Grade, (:<!>:)) )
import Math.Linear
( V, M )
import qualified SPIRV.PrimOp as SPIRV
( op )
import qualified SPIRV.PrimTy as SPIRV
( PrimTy )
import qualified SPIRV.Stage as SPIRV
( Backend(Vulkan) )
------------------------------------------------------------
infixl 9 :$
infixr 5 `ConsHList`
pattern Lam :: forall fun fs
. ( LamF :<! fs )
=> forall a b
. ( fun ~ ( Val a :--> b ) )
=> ( EGADT fs ( Val a ) -> EGADT fs b )
-> EGADT fs fun
pattern Lam f = VF (LamF f )
pattern (:$) f a = VF (AppF f a)
pattern Lit l = VF (LitF l )
pattern MkID i = VF (MkIDF i )
pattern Value v = VF (ValueF v )
pattern UnsafeCoerce = VF UnsafeCoerceF
pattern Return = VF ReturnF
pattern Bind = VF BindF
pattern PrimOp a op = VF (PrimOpF a op)
pattern Undefined :: forall a fs
. ( PrimTy a, UndefinedF :<! fs )
=> EGADT fs (Val a)
pattern Undefined = VF UndefinedF
pattern GradedMappend = VF GradedMappendF
pattern Pure px a = VF (PureF px a)
pattern Ap px f a = VF (ApF px f a)
pattern MkVector v = VF (MkVectorF v)
pattern Mat = VF MatF
pattern UnMat = VF UnMatF
pattern Struct st = VF (StructF st)
pattern Array arr = VF (ArrayF arr)
pattern ArrayLength px1 px2 = VF ( ArrayLengthF px1 px2 )
pattern NilHList = VF NilHListF
pattern ConsHList = VF ConsHListF
-- | Lambda abstraction.
data LamF ( ast :: AugType -> Type ) ( t :: AugType ) where
LamF :: ( ast (Val a) -> ast b ) -> LamF ast ( Val a :--> b )
-- | Function application.
data AppF ( ast :: AugType -> Type ) ( t :: AugType ) where
AppF :: ast ( a :--> b ) -> ast a -> AppF ast b
-- | Haskell-level constants can be embedded into the AST.
data LitF ( ast :: AugType -> Type ) ( t :: AugType ) where
LitF :: PrimTy a => a -> LitF ast (Val a)
-- | Create an object of the given type from its ID.
-- These are the internal @SPIR-V@ identifiers, in SSA form.
-- Used internally during code-generation (evaluation not possible).
data MkIDF ( ast :: AugType -> Type ) ( t :: AugType ) where
MkIDF :: ( ID, SPIRV.PrimTy ) -> MkIDF ast (Val a)
-- | Embed a value into the AST.
--
-- Used internallly to implement evaluation (code-generation not possible).
data ValueF ( ast :: AugType -> Type ) ( t :: AugType ) where
ValueF :: a -> ValueF ast (Val a)
-- | Coercions (unsafe).
data UnsafeCoerceF ( ast :: AugType -> Type ) ( t :: AugType ) where
UnsafeCoerceF :: UnsafeCoerceF ast (Val a :--> Val b)
data IxMonadF ( ast :: AugType -> Type ) ( t :: AugType ) where
-- Indexed monadic operations (for the AST itself).
-- | Indexed /return/
ReturnF :: IxMonadF ast (Val a :--> Eff i i a)
-- | Indexed /angelic bind/
BindF :: IxMonadF ast ( Eff i j a :--> ( Val a :--> q j ) :--> q i )
-- | @SPIR-V@ primitive operations.
data PrimOpF ( ast :: AugType -> Type ) ( t :: AugType ) where
PrimOpF :: PrimOp op a => Proxy a -> Proxy op -> PrimOpF ast (PrimOpAugType op a)
-- | Undefined (result of @SPIR-V@'s @OpUndef@).
data UndefinedF ( ast :: AugType -> Type ) ( t :: AugType ) where
UndefinedF :: PrimTy a => UndefinedF ast (Val a)
-- | Graded mappend to concatenate objects (e.g. two vectors).
data GradedMappendF ( ast :: AugType -> Type ) ( t :: AugType ) where
GradedMappendF
:: ( GradedSemigroup g k, a ~ Grade k g i, b ~ Grade k g j )
=> GradedMappendF ast ( Val a :--> Val b :--> Val (Grade k g (i :<!>: j)) )
data ApplicativeF ( ast :: AugType -> Type ) ( t :: AugType ) where
-- | pure/return for applicative functors within the AST.
PureF
:: forall f a v ast
. ( PrimFunc f
, All PrimTyVal (FunArgs a)
, FunRes a ~ Val v, PrimTy v
)
=> Proxy f
-> ast a
-> ApplicativeF ast ( ApplyFAug f a )
-- | ap/(<*>) for applicative functors within the AST.
ApF
:: forall f a b ast. ( PrimFunc f, PrimTy a )
=> Proxy f
-> ast ( Val (f a) :--> ApplyFAug f b )
-> ast ( Val (f a) )
-> ApplicativeF ast ( ApplyFAug f b )
-- | Create a vector from its components.
data MkVectorF ( ast :: AugType -> Type ) ( t :: AugType ) where
MkVectorF
:: ( KnownNat n, PrimTy a )
=> V n (ast (Val a))
-> MkVectorF ast (Val (V n a))
data MatF ( ast :: AugType -> Type ) ( t :: AugType ) where
-- | Newtype wrapping for matrices.
MatF
:: forall m n a ast
. ( KnownNat m, KnownNat n )
=> MatF ast ( Val (V n (V m a)) :--> Val (M m n a) )
-- | Newtype unwrapping for matrices.
UnMatF
:: forall m n a ast
. ( KnownNat n, KnownNat m )
=> MatF ast ( Val (M m n a) :--> Val (V n (V m a)) )
-- | Construct a structure from statically-known parts.
data StructF ( ast :: AugType -> Type ) ( t :: AugType ) where
StructF
:: ( PrimTyMap bs, ASTStructFields ast as bs, bs ~ StructFResultFromArgs as, as ~ StructFArgsFromResult bs ast)
=> Struct as
-> StructF ast ( Val (Struct bs) )
type family StructFResultFromArgs (as :: [ Symbol :-> Type ]) :: [ Symbol :-> Type ] where
StructFResultFromArgs '[] = '[]
StructFResultFromArgs ((k ':-> ast (Val b)) : as) = ((k ':-> b) : StructFResultFromArgs as)
type family StructFArgsFromResult (bs :: [ Symbol :-> Type ]) (ast :: AugType -> Type) :: [ Symbol :-> Type ] where
StructFArgsFromResult '[] ast = '[]
StructFArgsFromResult ((k ':-> b) : bs) ast = ((k ':-> ast (Val b)) : StructFArgsFromResult bs ast)
-- | Construct an array from its components.
data ArrayF ( ast :: AugType -> Type ) ( t :: AugType ) where
ArrayF
:: PrimTy a
=> Array n ( ast (Val a) )
-> ArrayF ast ( Val (Array n a) )
-- | Query length of a run-time array.
data ArrayLengthF ( ast :: AugType -> Type ) ( t :: AugType ) where
ArrayLengthF
:: ( KnownSymbol structName, KnownNat arrayPosition )
=> Proxy structName
-> Proxy arrayPosition
-> ArrayLengthF ast ( Eff i i Word32 )
-- | Internal HList data type.
--
-- Only used for providing multiple run-time indices to product optics.
-- See [FIR issue #13](https://gitlab.com/sheaf/fir/issues/13).
data HListF ( ast :: AugType -> Type ) ( t :: AugType ) where
ConsHListF
:: PrimTy a
=> HListF ast ( Val a :--> Val (HList as) :--> Val (HList (a ': as)) )
NilHListF
:: HListF ast ( Val ( HList '[] ) )
------------------------------------------------------------
-- displaying
instance ( ast ~ EGADT cs, MkIDF :<! cs, Display ast ) => Display (LamF ast) where
toTreeArgs as (LamF f) = do
v <- fresh
let var = MkID (v, undefined)
body <- toTreeArgs @_ @ast [] (f var)
return case as of
[] -> Node ("Lam " ++ show v) [body]
_ -> Node ":$" (body : as)
instance Display ast => Display (AppF ast) where
toTreeArgs as (f `AppF` a) = do
at <- toTreeArgs [] a
toTreeArgs (at:as) f
instance Display (LitF ast) where
toTreeArgs = named \ (LitF (a :: ty)) ->
"Lit @" ++ show (primTy @ty) ++ ") " ++ show a
instance Display (MkIDF ast) where
toTreeArgs = named ( \ (MkIDF (v,_)) -> show v )
instance Display (ValueF ast) where
toTreeArgs = error "internal error: unexpected 'Value' constructor, unsupported in pretty-printing"
instance Display (UnsafeCoerceF ast) where
toTreeArgs = named (const "UnsafeCoerce")
instance Display (IxMonadF ast) where
toTreeArgs = named \case
ReturnF -> "Return"
BindF -> "Bind"
instance Display (PrimOpF ast) where
toTreeArgs = named
( \( PrimOpF ( _ :: Proxy a ) ( _ :: Proxy op ) ) ->
-- use the Vulkan backend as a default for printing operation names
"PrimOp" ++ show ( SPIRV.op SPIRV.Vulkan ( opName @_ @_ @op @a ) )
)
instance Display (UndefinedF ast) where
toTreeArgs = named (const "Undefined")
instance Display (GradedMappendF ast) where
toTreeArgs = named (const "(<!>)")
instance Display ast => Display (ApplicativeF ast) where
toTreeArgs as (PureF ( _ :: Proxy f ) a) = do
at <- toTreeArgs [] a
pure $ Node ( "Pure @(" ++ primFuncName @f ++ ")" ) (at:as)
toTreeArgs as (ApF ( _ :: Proxy f ) f a) = do
ft <- toTreeArgs [] f
at <- toTreeArgs [] a
pure $ Node ( "(<*>) @(" ++ primFuncName @f ++ ")" ) (ft:at:as)
instance Display ast => Display (MkVectorF ast) where
toTreeArgs as (MkVectorF (vec :: V n (ast (Val a)))) = do
trees <- toList <$> traverse ( toTreeArgs @_ @ast []) vec
pure $ Node ("Vec" ++ show ( knownValue @n ) ) ( trees ++ as )
instance Display (MatF ast) where
toTreeArgs = named \case
MatF -> "Mat"
UnMatF -> "UnMat"
instance Display ast => Display (StructF ast) where
toTreeArgs as mkStruct@(StructF elts) = case mkStruct of
( _ :: StructF ast (Val (Struct bs)) ) -> do
trees <- traverseStructASTs (toTreeArgs @_ @ast []) elts
let
fieldNames :: [ (Maybe ShortText, SPIRV.PrimTy) ]
fieldNames = primTyMap @_ @bs
pure $ Node ("Struct @" ++ show fieldNames) (trees ++ as)
instance Display ast => Display (ArrayF ast) where
toTreeArgs as mkArray@(ArrayF (MkArray vec)) = case mkArray of
( _ :: ArrayF ast (Val (Array n a)) ) -> do
let
comps = Vector.toList vec
lg = Vector.knownLength' vec ( \px -> show ( natVal px ) )
trees <- traverse (toTreeArgs []) comps
pure $
Node
( "Array @" ++ lg ++ " @" ++ show (primTy @a) )
( trees ++ as )
instance Display (ArrayLengthF ast) where
toTreeArgs as (ArrayLengthF px_name px_pos) =
let
structName :: String
structName = symbolVal px_name
structPos :: Natural
structPos = natVal px_pos
in
pure $ Node ( "ArrayLength @" <> structName <> " @" <> show structPos ) as
instance Display (HListF ast) where
toTreeArgs = named \case
NilHListF -> "NilHList"
ConsHListF -> "ConsHList"