packages feed

fir-0.1.0.0: src/FIR/Binding.hs

{-# LANGUAGE DataKinds             #-}
{-# LANGUAGE DerivingStrategies    #-}
{-# LANGUAGE FlexibleInstances     #-}
{-# LANGUAGE GADTs                 #-}
{-# LANGUAGE MultiParamTypeClasses #-}
{-# LANGUAGE ScopedTypeVariables   #-}
{-# LANGUAGE TypeFamilies          #-}
{-# LANGUAGE TypeOperators         #-}
{-# LANGUAGE UndecidableInstances  #-}

{-|
Module: FIR.Binding

Keeping track of variable and function bindings at the type-level.
-}

module FIR.Binding
  ( Permission(Read,Write), Permissions
  , R, W, RW
  , Binding(Variable, Function)
  , Var, Fun
  , BindingsMap
  , FunctionType, FunctionAugType, FunArgTypes
  , StoragePermissions
  )
  where

-- base
import Data.Kind
  ( Type )
import GHC.TypeLits
  ( Symbol )
import GHC.TypeNats
  ( Nat )

-- fir
import Data.Type.Known
  ( Demotable(Demote), Known(known) )
import Data.Type.List
  ( Elem )
import Data.Type.Map
  ( (:->)((:->)), Map )
import FIR.AST.Type
  ( AugType(Val,(:-->)), UnderlyingType )
import qualified SPIRV.Decoration as SPIRV
  ( Decoration(NonReadable, NonWritable) )
import qualified SPIRV.Storage    as SPIRV

------------------------------------------------------------------------------------------------
-- bindings: variables, functions, images

data Permission = Read | Write
  deriving stock (Eq, Show)

type Permissions = [Permission]

instance Demotable Permission where
  type Demote Permission = Permission
instance Known Permission 'Read where
  known = Read
instance Known Permission 'Write where
  known = Write

type R  = '[ 'Read  ]
type W  = '[ 'Write ]
type RW = '[ 'Read, 'Write ]

-- | A binding consists of either:
--   * a variable of given type and permissions (readable/writable), or
--   * a function, with (named) arguments of given types, and given return type.
data Binding where
  Variable   :: Permissions -> Type -> Binding
  Function   :: [Symbol :-> Binding] -> Type -> Binding

-- shorthands
type Var ps a = 'Variable ps a
type Fun as b = 'Function as b

type BindingsMap = Map Symbol Binding

type FunctionType as b = UnderlyingType (FunctionAugType as b)

type family FunArgTypes (as :: BindingsMap) :: [Type] where
  FunArgTypes '[] = '[]
  FunArgTypes ((_ ':-> a) ': as) = UnderlyingType (BindingAugType a) ': FunArgTypes as

type family FunctionAugType (as :: BindingsMap) (b :: Type) :: AugType where
  FunctionAugType '[]                b = Val b
  FunctionAugType ((_ ':-> a) ': as) b = BindingAugType a :--> FunctionAugType as b

-- auxiliary type family (non-exported),
-- used only in the above 'FunctionType' type family
type family BindingAugType (bd :: Binding) :: AugType where
  BindingAugType (Var  _ a) = Val a
  BindingAugType (Fun as b) = FunctionAugType as b

------------------------------------------------------------------------------------------------
-- relation to SPIRV storage classes

type family StoragePermissions (storage :: SPIRV.StorageClass) (decs :: [SPIRV.Decoration Nat]) :: Permissions where
  StoragePermissions SPIRV.UniformConstant  _    = R
  StoragePermissions SPIRV.Input            _    = R
  StoragePermissions SPIRV.Uniform          _    = R
  StoragePermissions SPIRV.Output           _    = W
  StoragePermissions SPIRV.Workgroup        decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.CrossWorkgroup   decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.Private          decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.Function         _    = RW -- default value, specific functions can specialise this
  StoragePermissions SPIRV.Generic          decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.PushConstant     _    = R
  StoragePermissions SPIRV.AtomicCounter    decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.Image            decs = PermissionsFromDecorations decs
  StoragePermissions SPIRV.StorageBuffer    decs = PermissionsFromDecorations decs
  StoragePermissions ('SPIRV.RayStorage SPIRV.HitAttribute ) decs = RW
  StoragePermissions ('SPIRV.RayStorage SPIRV.ShaderRecordBuffer ) _ = R
  StoragePermissions ('SPIRV.RayStorage _ ) decs = PermissionsFromDecorations decs


{-
TODO:
Variables with storage class HitAttribute:

They can be written to only in Intersection execution model,
and read from only in AnyHit and ClosestHit execution models.
-}

type family PermissionsFromDecorations (decs :: [SPIRV.Decoration Nat]) :: Permissions where
  PermissionsFromDecorations decs =
    PermissionsFromDecorations'
      ( SPIRV.NonReadable `Elem` decs )
      ( SPIRV.NonWritable `Elem` decs )

type family PermissionsFromDecorations' (nonReadable :: Bool) (nonWritable :: Bool) :: Permissions where
  PermissionsFromDecorations' True  True  = '[]
  PermissionsFromDecorations' True  False = W
  PermissionsFromDecorations' False True  = R
  PermissionsFromDecorations' False False = RW