halide-haskell-0.0.1.0: src/Language/Halide/Trace.hs
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TupleSections #-}
{-# LANGUAGE TypeApplications #-}
-- |
-- Module : Language.Halide.Trace
-- Copyright : (c) Tom Westerhout, 2023
module Language.Halide.Trace
( TraceEvent (..)
, TraceEventCode (..)
, TraceLoadStoreContents (..)
, setCustomTrace
, traceStores
, traceLoads
, collectIterationOrder
)
where
import Control.Concurrent.MVar
import Control.Exception (bracket, bracket_)
import Data.ByteString (packCString)
import Data.Int (Int32)
import Data.Text (Text)
import Data.Text.Encoding (decodeUtf8)
import Foreign.Marshal (peekArray)
import Foreign.Ptr (FunPtr, Ptr, freeHaskellFunPtr)
import Foreign.Storable
import GHC.TypeLits
import qualified Language.C.Inline as C
import qualified Language.C.Inline.Unsafe as CU
import Language.Halide.Buffer
import Language.Halide.Context
import Language.Halide.Dimension
import Language.Halide.Func
import Language.Halide.LoopLevel
import Language.Halide.Type
import Prelude hiding (min, tail)
-- | Haskell counterpart of [@halide_trace_event_code_t@](https://halide-lang.org/docs/_halide_runtime_8h.html#a485130f12eb8bb5fa5a9478eeb6b0dfa).
data TraceEventCode
= TraceLoad
| TraceStore
| TraceBeginRealization
| TraceEndRealization
| TraceProduce
| TraceEndProduce
| TraceConsume
| TraceEndConsume
| TraceBeginPipeline
| TraceEndPipeline
| TraceTag
deriving stock (Show, Eq, Ord)
data TraceLoadStoreContents = TraceLoadStoreContents
{ valuePtr :: !(Ptr ())
, valueType :: !HalideType
, coordinates :: ![Int]
}
deriving stock (Show)
data TraceEvent = TraceEvent
{ funcName :: !Text
, eventCode :: !TraceEventCode
, loadStoreContents :: !(Maybe TraceLoadStoreContents)
}
deriving stock (Show)
importHalide
instance Enum TraceEventCode where
fromEnum =
fromIntegral . \case
TraceLoad -> [CU.pure| int { halide_trace_load } |]
TraceStore -> [CU.pure| int { halide_trace_store } |]
TraceBeginRealization -> [CU.pure| int { halide_trace_begin_realization } |]
TraceEndRealization -> [CU.pure| int { halide_trace_end_realization } |]
TraceProduce -> [CU.pure| int { halide_trace_produce } |]
TraceEndProduce -> [CU.pure| int { halide_trace_end_produce } |]
TraceConsume -> [CU.pure| int { halide_trace_consume } |]
TraceEndConsume -> [CU.pure| int { halide_trace_end_consume } |]
TraceBeginPipeline -> [CU.pure| int { halide_trace_begin_pipeline } |]
TraceEndPipeline -> [CU.pure| int { halide_trace_end_pipeline } |]
TraceTag -> [CU.pure| int { halide_trace_tag } |]
toEnum k
| fromIntegral k == [CU.pure| int { halide_trace_load } |] = TraceLoad
| fromIntegral k == [CU.pure| int { halide_trace_store } |] = TraceStore
| fromIntegral k == [CU.pure| int { halide_trace_begin_realization } |] = TraceBeginRealization
| fromIntegral k == [CU.pure| int { halide_trace_end_realization } |] = TraceEndRealization
| fromIntegral k == [CU.pure| int { halide_trace_produce } |] = TraceProduce
| fromIntegral k == [CU.pure| int { halide_trace_end_produce } |] = TraceEndProduce
| fromIntegral k == [CU.pure| int { halide_trace_consume } |] = TraceConsume
| fromIntegral k == [CU.pure| int { halide_trace_end_consume } |] = TraceEndConsume
| fromIntegral k == [CU.pure| int { halide_trace_begin_pipeline } |] = TraceBeginPipeline
| fromIntegral k == [CU.pure| int { halide_trace_end_pipeline } |] = TraceEndPipeline
| fromIntegral k == [CU.pure| int { halide_trace_tag } |] = TraceTag
| otherwise = error $ "invalid TraceEventCode: " <> show k
peekTraceLoadStoreContents :: Ptr TraceEvent -> IO TraceLoadStoreContents
peekTraceLoadStoreContents p = do
v <- [CU.exp| void* { $(const halide_trace_event_t* p)->value } |]
tp <- peek =<< [CU.exp| const halide_type_t* { &$(const halide_trace_event_t* p)->type } |]
n <- fromIntegral <$> [CU.exp| int { $(const halide_trace_event_t* p)->dimensions } |]
cs <- peekArray n =<< [CU.exp| const int32_t* { $(const halide_trace_event_t* p)->coordinates } |]
pure $ TraceLoadStoreContents v tp (fromIntegral <$> cs)
peekTraceEvent :: Ptr TraceEvent -> IO TraceEvent
peekTraceEvent p = do
f <-
fmap decodeUtf8 $
packCString
=<< [CU.exp| const char* { $(const halide_trace_event_t* p)->func } |]
c <- toEnum . fromIntegral <$> [CU.exp| int { $(const halide_trace_event_t* p)->event } |]
contents <-
case c of
TraceLoad -> Just <$> peekTraceLoadStoreContents p
TraceStore -> Just <$> peekTraceLoadStoreContents p
_ -> pure Nothing
pure $ TraceEvent f c contents
withTrace
:: (TraceEvent -> IO ()) -> (FunPtr (Ptr CxxUserContext -> Ptr TraceEvent -> IO Int32) -> IO a) -> IO a
withTrace customTrace = bracket allocate destroy
where
allocate = do
$(C.mkFunPtr [t|Ptr CxxUserContext -> Ptr TraceEvent -> IO Int32|]) $ \_ p ->
peekTraceEvent p >>= customTrace >> pure 0
destroy = freeHaskellFunPtr
setCustomTrace
:: (KnownNat n, IsHalideType a)
=> (TraceEvent -> IO ()) -- ^ Custom trace function
-> Func t n a -- ^ For which func to enable it
-> IO b -- ^ For the duration of which computation to enable it
-> IO b
setCustomTrace customTrace f action =
withTrace customTrace $ \tracePtr ->
bracket_ (set tracePtr) unset action
where
set tracePtr =
withFunc f $ \f' ->
[CU.block| void {
auto& func = *$(Halide::Func* f');
func.jit_handlers().custom_trace = $(int32_t (*tracePtr)(Halide::JITUserContext*, const halide_trace_event_t*));
} |]
unset =
withFunc f $ \f' ->
[CU.block| void {
auto& func = *$(Halide::Func* f');
func.jit_handlers().custom_trace = nullptr;
} |]
traceStores :: (KnownNat n, IsHalideType a) => Func t n a -> IO (Func t n a)
traceStores f = do
withFunc f $ \f' ->
[CU.exp| void { $(Halide::Func* f')->trace_stores() } |]
pure f
traceLoads :: (KnownNat n, IsHalideType a) => Func t n a -> IO (Func t n a)
traceLoads f = do
withFunc f $ \f' ->
[CU.exp| void { $(Halide::Func* f')->trace_loads() } |]
pure f
collectIterationOrder
:: (KnownNat n, IsHalideType a)
=> (TraceEventCode -> Bool)
-> Func t n a
-> IO b
-> IO ([[Int]], b)
collectIterationOrder cond f action = do
m <- newMVar []
let tracer (TraceEvent _ c' (Just payload))
| cond c' = modifyMVar_ m $ pure . (payload.coordinates :)
tracer _ = pure ()
setCustomTrace tracer f $ do
r <- action
cs <- readMVar m
pure (reverse cs, r)