fir-0.1.0.0: src/CodeGen/CodeGen.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE DerivingVia #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE StandaloneDeriving #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-|
Module: CodeGen.CodeGen
Main code generation function: dispatches code generation on the constructors of the AST.
-}
module CodeGen.CodeGen
( CodeGen(codeGenArgs), codeGen, runCodeGen )
where
-- base
import Prelude
hiding ( Monad(..) )
import Data.Kind
( Type )
import Data.Proxy
( Proxy )
import Data.Type.Equality
( (:~:)(Refl) )
import qualified GHC.Stack
( callStack )
import Unsafe.Coerce
( unsafeCoerce )
-- bytestring
import Data.ByteString.Lazy
( ByteString )
-- mtl
import Control.Monad.Except
( throwError )
-- text-short
import Data.Text.Short
( ShortText )
-- variant
import Data.Variant.VariantF
( VariantF, ApplyAll, BottomUp(toBottomUp) )
import Data.Variant.EGADT
( EGADT(..), HVariantF(..) )
-- fir
import CodeGen.Application
( Application(Applied)
, pattern Nullary
, ASTs(NilAST, ConsAST)
)
import CodeGen.Applicative
( ) -- CodeGen instances for Ap, Pure
import CodeGen.Binary
( putModule )
import CodeGen.CFG
( locally ) -- + CodeGen instances for If, IfM, Switch, SwitchM, While, Locally, Embed
import CodeGen.Composite
( ) -- CodeGen instances for MkVector, Mat, UnMat, Struct, Array, GradedMappend
import CodeGen.Debug
( whenDebugging, putSrcInfo ) -- CodeGen instance for DebugPrintF
import CodeGen.Functions
( ) -- CodeGen instances for FunDef, FunCall, DefEntryPoint
import CodeGen.IDs
( constID, undefID )
import CodeGen.Images
( ) -- CodeGen instances for image operands
import CodeGen.Instruction
( ID, Pointer(..) )
import CodeGen.Pointers
( newVariable, store )
import CodeGen.PrimOps
( ) -- CodeGen instance for PrimOp
import CodeGen.RayTracing
( ) -- Codegen instances for TraceRay, ExecuteCallable, RayQuery
import CodeGen.Monad
( CGMonad, runCGMonad
, runExceptTPutM
)
import CodeGen.Optics
( ) -- CodeGen for View, Set, Use, Assign
import CodeGen.State
( CGState, CGContext
, initialState
, currentFunctionContext
, defineLocalBinding
)
import Data.Type.Known
( knownValue )
import FIR.AST
( AST
, pattern (:$), pattern Lam, pattern MkID
, AppF(..), LamF(..), LitF(..), MkIDF(..), ValueF(..)
, IxMonadF(..)
, LetF(..), DefF(..), UnsafeCoerceF(..)
, HListF(..)
, UndefinedF(..)
)
import FIR.AST.Type
( AugType(Val, (:-->)), FunArgs, Nullary )
import FIR.Binding
( Permission(Write) )
import FIR.Prim.Types
( primTy )
import FIR.ProgramState
( FunctionContext(TopLevel) )
import FIR.Syntax.AST
( )
import qualified SPIRV.PrimTy as SPIRV
----------------------------------------------------------------------------
-- run the code generator with given context to create a SPIR-V module
runCodeGen :: Nullary a => CGContext -> AST a -> Either ShortText (ByteString, CGState)
runCodeGen context ast
= case runCGMonad context (initialState context) (codeGen ast) of
Right (_, cgState, body)
-> case runExceptTPutM $ putModule context cgState of
Right ((), decs) -> Right ( decs <> body, cgState )
Left err -> Left err
Left err -> Left err
----------------------------------------------------------------------------
-- typeclass to dispatch code generation on AST constructors
class CodeGen (ast :: AugType -> Type) where
codeGenArgs :: Nullary r => Application ast f r -> CGMonad (ID, SPIRV.PrimTy)
codeGen :: ( CodeGen ast, Nullary v ) => ast v -> CGMonad (ID, SPIRV.PrimTy)
codeGen v = codeGenArgs (Nullary v)
instance BottomUp CodeGen vs => CodeGen (VariantF vs) where
codeGenArgs (Applied f args) =
toBottomUp @CodeGen ( \x -> codeGenArgs $ Applied x args ) f
deriving via VariantF ( ApplyAll (EGADT vs :: AugType -> Type) vs )
instance BottomUp CodeGen ( ApplyAll (EGADT vs) vs )
=> CodeGen (EGADT vs)
----------------------------------------------------------------------------
-- some instances
instance CodeGen AST => CodeGen (LamF AST) where
codeGenArgs (Applied (LamF f) (a `ConsAST` as)) = do
cg <- codeGen a
codeGenArgs ( Applied ( f (MkID cg) ) as )
instance CodeGen (LitF AST) where
codeGenArgs (Applied (LitF (a :: ty)) NilAST) = ( , primTy @ty ) <$> constID a
instance CodeGen AST => CodeGen (MkIDF AST) where
codeGenArgs (Applied (MkIDF idTy) NilAST) = pure idTy
-- TODO: "Value" should not be a constructor of the AST
-- we should internally change the AST to add the Value constructor when doing evaluation
instance CodeGen (ValueF AST) where
codeGenArgs _ = throwError "codeGen: unsupported embedded value"
instance CodeGen AST => CodeGen (AppF AST) where
codeGenArgs (Applied (AppF (Lam f) a) as) = codeGenArgs (Applied (f a) as)
codeGenArgs (Applied (AppF f a) as) = codeGenArgs (Applied f (a `ConsAST` as))
instance CodeGen AST => CodeGen (IxMonadF AST) where
codeGenArgs (Applied ReturnF (a `ConsAST` NilAST)) = codeGen a
codeGenArgs (Applied BindF (a `ConsAST` f `ConsAST` as))
| ( _ :: AST ( Val a :--> q j ) ) <- f
, Refl <- ( unsafeCoerce Refl :: FunArgs (q j) :~: '[] )
= do
cg <- codeGen a
codeGenArgs ( Applied ( f :$ MkID cg ) as )
instance CodeGen AST => CodeGen (UnsafeCoerceF AST) where
codeGenArgs ( Applied UnsafeCoerceF (a `ConsAST` NilAST) ) = codeGenArgs ( Nullary a )
instance CodeGen AST => CodeGen (UndefinedF AST) where
codeGenArgs ( Applied undef@UndefinedF NilAST ) = case undef of
( _ :: UndefinedF AST ( Val a ) ) ->
let undefTy = primTy @a
in ( , undefTy ) <$> undefID undefTy
instance CodeGen AST => CodeGen (HListF AST) where
codeGenArgs _ = throwError "codeGen: internal error, cannot generate code for HList operations"
instance CodeGen AST => CodeGen (LetF AST) where
codeGenArgs (Applied LetF (a `ConsAST` NilAST)) = locally (codeGen a)
instance CodeGen AST => CodeGen (DefF AST) where
codeGenArgs (Applied (DefF (_ :: Proxy name) (_ :: Proxy ps)) (a `ConsAST` NilAST)) = do
let name = knownValue @name
writable = Write `elem` (knownValue @ps)
whenDebugging ( putSrcInfo GHC.Stack.callStack )
cgRes@(a_ID, a_ty) <- codeGen a
-- check if we should make this into a pointer or not
let
makeMutable =
case a_ty of
SPIRV.Array {} -> writable
SPIRV.RuntimeArray {} -> writable -- should never happen, user cannot define runtime arrays
SPIRV.Struct {} -> writable
_ -> False
if makeMutable
then do
ctx <- currentFunctionContext
case ctx of
TopLevel ->
throwError ( "codeGen: cannot define mutable variable " <> name <> " at the top level" )
_ -> pure ()
ptr@( Pointer ptrID ptrTy ) <- newVariable a_ty
store name ptr a_ID
defineLocalBinding name (ptrID, SPIRV.Pointer ptrTy) False
else
defineLocalBinding name cgRes writable
pure cgRes