packages feed

fir-0.1.0.0: src/CodeGen/Instruction.hs

{-# LANGUAGE BlockArguments             #-}
{-# LANGUAGE DerivingStrategies         #-}
{-# LANGUAGE GADTs                      #-}
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
{-# LANGUAGE PatternSynonyms            #-}
{-# LANGUAGE RankNTypes                 #-}
{-# LANGUAGE ScopedTypeVariables        #-}
{-# LANGUAGE StandaloneDeriving         #-}

{-|
Module: CodeGen.Instruction

This module defines an 'Instruction' data type which collects the information associated to a SPIR-V instruction:
  
  * which instruction it is (essentially: the instruction's OpCode),
  * the ID of the result returned by the instruction (if any),
  * the ID of the return type of the instruction (if any),
  * the arguments to the instruction (usually IDs, but can be other things such as string literals).
-}


module CodeGen.Instruction
  ( Args(..), foldrArgs, arity, toArgs, Pair(..)
  , ID(..), TyID(MkTyID, TyID, tyID)
  , Instruction(..)
  , prependArg, setArgs
  , Pointer(..), Variable(..), variableID, variableDeclaration
  ) where

-- base
import Control.Category
  ( (>>>) )
import Data.Word
  ( Word32 )

-- fir
import Data.Binary.Class.Put
  ( Put(..) )
import qualified SPIRV.Operation as SPIRV
import qualified SPIRV.PrimTy    as SPIRV
  ( PointerTy(..) )

----------------------------------------------------------------------------
-- args

data Args where
  EndArgs :: Args
  Arg     :: ( Show a, Put a ) => a -> Args -> Args

deriving stock instance Show Args

-- mono-foldable business
foldrArgs :: (forall a. (Show a, Put a) => a -> b -> b) -> b -> Args -> b
foldrArgs _ b0 EndArgs    = b0
foldrArgs f b0 (Arg a as) = f a ( foldrArgs f b0 as )

instance Put Args where
  put = foldrArgs (put >>> (>>)) (pure ())
  wordCount = foldrArgs ( (+) . wordCount ) 0

arity :: Num a => Args -> a
arity = foldrArgs (const (+1)) 0

toArgs :: ( Show a, Put a, Foldable t ) => t a -> Args
toArgs = foldr Arg EndArgs

newtype Pair a b = Pair (a,b)
  deriving stock Show
instance ( Put a, Put b ) => Put (Pair a b) where
  put (Pair (a,b)) = put a *> put b
  wordCount (Pair (a,b)) = wordCount a + wordCount b

----------------------------------------------------------------------------
-- instructions

newtype ID = ID { idNumber :: Word32 }
  deriving newtype ( Eq, Ord, Enum, Put )

instance Show ID where
  show (ID n) = "%" ++ show n

newtype TyID = TyID { tyID :: ID }
  deriving newtype ( Eq, Ord, Enum, Put, Show )

pattern MkTyID :: Word32 -> TyID
pattern MkTyID i = TyID (ID i)

data Instruction
  = Instruction
    { operation :: SPIRV.Operation
    , resTy     :: Maybe TyID
    -- making 'resTy' and 'resID' of different types prevents accidentally mixing them up
    , resID     :: Maybe ID
    , args      :: Args
    }
  deriving stock Show

prependArg :: ( Show a, Put a ) => a -> Instruction -> Instruction
prependArg arg instr@Instruction { args = oldArgs }
  = instr { args = Arg arg oldArgs }

setArgs :: Args -> Instruction -> Instruction
setArgs as instr = instr { args = as }

----------------------------------------------------------------------------
-- pointers and variables

-- | A pointer, together with its type.
data Pointer
  = Pointer
    { pointerID :: ID
    , pointerTy :: SPIRV.PointerTy
    }
  deriving stock Show

-- | A variable: a pointer declared with an @OpVariable@ instruction,
-- together with the ID of its type.
data Variable
  = Variable
    { variablePointer :: Pointer
    , variableTyID    :: TyID
    }
  deriving stock Show

-- | The ID of a variable.
variableID :: Variable -> ID
variableID = pointerID . variablePointer

-- | The instruction declaring a variable.
variableDeclaration :: Variable -> Instruction
variableDeclaration ( Variable ( Pointer v ( SPIRV.PointerTy storage _ _ ) ) ptrTyID )
  = Instruction
      { operation = SPIRV.Variable
      , resTy     = Just ptrTyID
      , resID     = Just v
      , args      = Arg storage EndArgs
      }