packages feed

fir-0.1.0.0: src/CodeGen/Debug.hs

{-# LANGUAGE BlockArguments    #-}
{-# LANGUAGE FlexibleContexts  #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE GADTs             #-}
{-# LANGUAGE OverloadedStrings #-}
{-# LANGUAGE TypeApplications  #-}

{-|
Module: CodeGen.Debug

Functionality for including extra debugging information as comments in SPIR-V modules,
such as including the line numbers of the Haskell source code.
-}

module CodeGen.Debug
  ( whenDebugging, whenAsserting
  , putSrcInfo
  )
  where

-- base
import Control.Monad
  ( when )
import Data.Foldable
  ( for_ )
import Data.Word
  ( Word32 )
import qualified GHC.Stack
import qualified GHC.Stack.Types as GHC.Stack

-- lens
import Control.Lens
  ( view )

-- mtl
import Control.Monad.Reader
  ( MonadReader )

-- text-short
import Data.Text.Short
  ( ShortText )
import qualified Data.Text.Short as ShortText
  ( pack )

-- fir
import CodeGen.Application
  ( Application(Applied)
  , traverseASTs
  )
import CodeGen.Binary
  ( instruction )
import {-# SOURCE #-} CodeGen.CodeGen
  ( CodeGen(codeGenArgs), codeGen )
import CodeGen.IDs
  ( stringLitID, typeID )
import CodeGen.Instruction
  ( ID(..), Args(..), toArgs
  , Instruction(..)
  )
import CodeGen.Monad
  ( MonadFresh(fresh), CGMonad, note )
import CodeGen.State
  ( CGContext
  , _debugging
  , _asserting
  , requireExtension
  , currentBlockID
  )
import FIR.AST
  ( AST, DebugPrintfF(..) )
import FIR.AST.Type
  ( Nullary )
import qualified SPIRV.Extension as SPIRV
import qualified SPIRV.Operation as SPIRV.Op
import qualified SPIRV.PrimTy    as SPIRV

----------------------------------------------------------------------------
-- debug printf

instance CodeGen AST => CodeGen (DebugPrintfF AST) where
  codeGenArgs ( Applied ( DebugPrintfF formatString ) as ) = do
    vals <- traverseASTs @Nullary ( fmap fst . codeGen ) as
    debugPrintf formatString vals

debugPrintf :: ShortText -> [ ID ] -> CGMonad (ID, SPIRV.PrimTy)
debugPrintf formatString vals = do
  whenDebugging do
    requireExtension SPIRV.SPV_KHR_non_semantic_info
    unitTyID <- typeID SPIRV.Unit
    v <- fresh
    formatStringID <- stringLitID formatString
    instruction
      Instruction
        { operation = SPIRV.Op.DebugPrintf
        , resTy = Just unitTyID
        , resID = Just v
        , args  = Arg formatStringID
                $ toArgs vals
        }
  pure (ID 0, SPIRV.Unit) -- ID should not be used

----------------------------------------------------------------------------
-- debugging annotations

whenDebugging :: MonadReader CGContext m => m () -> m ()
whenDebugging action = (`when` action) =<< view _debugging

whenAsserting :: MonadReader CGContext m => m () -> m ()
whenAsserting action = (`when` action) =<< view _asserting

sourceInfo :: GHC.Stack.CallStack -> Maybe (ShortText, Word32, Word32)
sourceInfo GHC.Stack.EmptyCallStack = Nothing
sourceInfo (GHC.Stack.PushCallStack _ loc stack)
  = case sourceInfo stack of
      Nothing
        -> Just ( ShortText.pack $ GHC.Stack.srcLocFile      loc
                , fromIntegral   $ GHC.Stack.srcLocStartLine loc
                , fromIntegral   $ GHC.Stack.srcLocStartCol  loc
                )
      Just info
        -> Just info
sourceInfo (GHC.Stack.FreezeCallStack stack) = sourceInfo stack

putSrcInfo :: GHC.Stack.CallStack -> CGMonad ()
putSrcInfo callstack
  = do
      -- (no debug information outside of blocks)
      mbBlock <- currentBlockID
      for_ mbBlock \ _ -> do
        (fileName, lineNo, colNo)
          <- note "putSrcInfo: cannot find source location \
                  \needed for debug statement"
                  ( sourceInfo callstack )
        fileID <- stringLitID fileName
        instruction
          Instruction
            { operation = SPIRV.Op.Line
            , resTy = Nothing
            , resID = Nothing
            , args  = Arg fileID
                    $ Arg lineNo
                    $ Arg colNo EndArgs
            }