fir-0.1.0.0: src/CodeGen/Functions.hs
{-# LANGUAGE BlockArguments #-}
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE PatternSynonyms #-}
{-# LANGUAGE RecursiveDo #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE UndecidableInstances #-}
{-|
Module: CodeGen.Functions
Code generation for function and entry-point definitions, and function calls.
-}
module CodeGen.Functions ( ) where
-- base
import Data.Foldable
( traverse_ )
import Data.Maybe
( fromMaybe )
import Data.Proxy
( Proxy )
import Data.Word
( Word32 )
import qualified GHC.Stack
( callStack )
-- containers
import qualified Data.Map.Strict as Map
-- lens
import Control.Lens
( view, use, assign )
-- text-short
import Data.Text.Short
( ShortText )
-- fir
import CodeGen.Application
( ASTs(NilAST, ConsAST)
, Application(Applied)
, traverseASTs
)
import CodeGen.Binary
( instruction, structuralInstruction, deferredInstructions )
import {-# SOURCE #-} CodeGen.CodeGen
( CodeGen(codeGenArgs), codeGen )
import CodeGen.CFG
( newBlock )
import CodeGen.Debug
( whenDebugging, putSrcInfo )
import CodeGen.IDs
( typeID )
import CodeGen.Instruction
( Args(..), toArgs
, ID, TyID
, Instruction(..)
, variableDeclaration
)
import CodeGen.Monad
( CGMonad
, MonadFresh(fresh)
, note
, createIDRec
)
import CodeGen.State
( FunctionState(localVariables)
, _knownBinding
, defineLocalBinding
, scoped
, withFunctionState
, _interface
, _entryPoint
, _userFunction
, requireCapabilities
)
import Data.Type.Known
( knownValue )
import FIR.AST
( AST
, FunDefF(..), FunCallF(..), DefEntryPointF(..)
)
import FIR.AST.Type
( Nullary )
import FIR.Binding
( Permission(Write), Permissions )
import FIR.Prim.Types
( primTy, knownVars )
import FIR.ProgramState
( FunctionContext(..)
, VLFunctionContext
, VLInterface
)
import qualified SPIRV.Control as SPIRV
( FunctionControl, pattern NoFunctionControl )
import qualified SPIRV.Operation as SPIRV.Op
import qualified SPIRV.PrimTy as SPIRV
( PrimTy(..) )
import qualified SPIRV.Requirements as SPIRV
( executionModelCapabilities )
import qualified SPIRV.Stage as SPIRV
( ExecutionModel, ExecutionInfo
, modelOf
)
----------------------------------
-- code-generation for functions and entry points
instance CodeGen AST => CodeGen (FunDefF AST) where
codeGenArgs (Applied (FunDefF (_ :: Proxy name) (_ :: Proxy as) (_ :: Proxy b)) (body `ConsAST` NilAST)) =
let as = knownVars @as
retTy = primTy @b
name = knownValue @name
in do
whenDebugging ( putSrcInfo GHC.Stack.callStack )
control <- fromMaybe SPIRV.NoFunctionControl <$> view ( _userFunction name )
funID <- declareFunction name control as retTy (codeGen body)
pure ( funID , SPIRV.Function (map (fst . snd) as) retTy )
instance CodeGen AST => CodeGen (FunCallF AST) where
codeGenArgs (Applied (FunCallF (_ :: Proxy name) ( _ :: Proxy as ) ( _ :: Proxy b )) (_ `ConsAST` as)) = do
let
funName = knownValue @name
retTy = primTy @b
retTyID <- typeID retTy
mbFunc <- use ( _knownBinding funName )
func <- note
( "codeGen: function " <> funName
<> "\" not bound to any ID."
)
mbFunc
retID <-
declareFunctionCall
( retTyID, retTy )
func
=<< traverseASTs @Nullary codeGen as
pure ( retID, retTy )
instance CodeGen AST => CodeGen (DefEntryPointF AST) where
codeGenArgs (Applied (DefEntryPointF (_ :: Proxy name) (_ :: Proxy stageInfo)) (body `ConsAST` NilAST))
= let name = knownValue @name
stageInfo = knownValue @stageInfo
in do
whenDebugging ( putSrcInfo GHC.Stack.callStack )
entryPointID <- declareEntryPoint name stageInfo Nothing (codeGen body) -- TODO
pure ( entryPointID, SPIRV.Function [] SPIRV.Unit )
----------------------------------
-- dealing with function context
inFunctionContext :: ShortText -> [(ShortText, (SPIRV.PrimTy, Permissions))] -> CGMonad a -> CGMonad a
inFunctionContext functionName as
= inContext ( InFunction functionName as ) as
inEntryPointContext
:: ShortText
-> SPIRV.ExecutionInfo Word32 model
-> Maybe VLInterface
-> CGMonad a
-> CGMonad a
inEntryPointContext modelName modelInfo mbIface
= inContext ( InEntryPoint modelName modelInfo mbIface ) []
inContext :: forall a. VLFunctionContext -> [(ShortText, (SPIRV.PrimTy, Permissions))] -> CGMonad a -> CGMonad a
inContext context as body = mdo
-- Use 'mdo' (and laziness) to declare local variables used in the function
-- body (discovered only after generating code for the function body).
( ( a, _ ), funState ) <-
withFunctionState context $ scoped do
traverse_ (uncurry declareArgument) as
newBlock
deferredInstructions
( map variableDeclaration ( Map.elems ( localVariables funState ) ) )
body
pure a
----------------------------------
-- declaring function instructions
declareFunctionCall :: (TyID, SPIRV.PrimTy)
-> (ID, SPIRV.PrimTy)
-> [ (ID, SPIRV.PrimTy) ]
-> CGMonad ID
declareFunctionCall res func argIDs
= do v <- fresh
instruction
Instruction
{ operation = SPIRV.Op.FunctionCall
, resTy = Just (fst res)
, resID = Just v
, args = Arg (fst func)
$ toArgs (map fst argIDs)
}
pure v
declareFunction :: ShortText
-> SPIRV.FunctionControl
-> [(ShortText, (SPIRV.PrimTy, Permissions))]
-> SPIRV.PrimTy
-> CGMonad (ID, SPIRV.PrimTy)
-> CGMonad ID
declareFunction funName control as b body
= createIDRec ( _knownBinding funName )
( do resTyID <- typeID b
fnTyID <- typeID funTy
pure (resTyID, fnTyID)
)
( \(resTyID,fnTyID) v -> do
structuralInstruction
Instruction
{ operation = SPIRV.Op.Function
, resTy = Just resTyID
, resID = Just v
, args = Arg control
$ Arg fnTyID EndArgs
}
(retValID, _) <- inFunctionContext funName as body
case b of
SPIRV.Unit
-> instruction
Instruction
{ operation = SPIRV.Op.Return
, resTy = Nothing
, resID = Nothing
, args = EndArgs
}
_ -> instruction
Instruction
{ operation = SPIRV.Op.ReturnValue
, resTy = Nothing
, resID = Just retValID
, args = EndArgs
}
structuralInstruction
Instruction
{ operation = SPIRV.Op.FunctionEnd
, resTy = Nothing
, resID = Nothing
, args = EndArgs
}
pure (v, funTy)
)
where
funTy :: SPIRV.PrimTy
funTy = SPIRV.Function ( map (fst . snd) as ) b
declareArgument :: ShortText -> (SPIRV.PrimTy, Permissions) -> CGMonad ID
declareArgument argName (argTy, permissions)
= do
argTyID <- typeID argTy
v <- fresh
structuralInstruction
Instruction
{ operation = SPIRV.Op.FunctionParameter
, resTy = Just argTyID
, resID = Just v
, args = EndArgs
}
let
writable = case argTy of
SPIRV.Pointer {} -> False
_ -> Write `elem` permissions
defineLocalBinding argName (v, argTy) writable
pure v
declareEntryPoint
:: ShortText
-> SPIRV.ExecutionInfo Word32 model
-> Maybe VLInterface
-> CGMonad r
-> CGMonad ID
declareEntryPoint modelName modelInfo mbIface body
= createIDRec ( _knownBinding modelName )
( do unitTyID <- typeID SPIRV.Unit
fnTyID <- typeID funTy
pure (unitTyID, fnTyID)
)
( \(unitTyID,fnTyID) v -> do
-- declare entry point ID proper
assign ( _entryPoint modelName model ) (Just v)
-- initialise entry point with empty interface
-- (loading/storing should add to the interface as needed)
assign ( _interface modelName model ) (Just Map.empty)
-- set the required capabilities
requireCapabilities ( SPIRV.executionModelCapabilities model )
structuralInstruction
Instruction
{ operation = SPIRV.Op.Function
, resTy = Just unitTyID
, resID = Just v
, args = Arg SPIRV.NoFunctionControl
$ Arg fnTyID EndArgs
}
_ <- inEntryPointContext modelName modelInfo mbIface body
instruction
Instruction
{ operation = SPIRV.Op.Return
, resTy = Nothing
, resID = Nothing
, args = EndArgs
}
structuralInstruction
Instruction
{ operation = SPIRV.Op.FunctionEnd
, resTy = Nothing
, resID = Nothing
, args = EndArgs
}
pure (v, funTy)
)
where
model :: SPIRV.ExecutionModel
model = SPIRV.modelOf modelInfo
funTy :: SPIRV.PrimTy
funTy = SPIRV.Function [] SPIRV.Unit