packages feed

halide-haskell-0.0.1.0: src/Language/Halide/Dimension.hs

{-# LANGUAGE AllowAmbiguousTypes #-}
{-# LANGUAGE InstanceSigs #-}
{-# LANGUAGE LambdaCase #-}
{-# LANGUAGE OverloadedRecordDot #-}
{-# LANGUAGE QuasiQuotes #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TemplateHaskell #-}
{-# LANGUAGE TypeApplications #-}
{-# OPTIONS_GHC -Wno-redundant-constraints #-}

-- |
-- Module      : Language.Halide.Dimension
-- Copyright   : (c) Tom Westerhout, 2023
module Language.Halide.Dimension
  ( Dimension (..)
  , setMin
  , setExtent
  , setStride
  , setEstimate

    -- * Internal
  , CxxDimension
  , wrapCxxDimension
  , withCxxDimension
  )
where

import Foreign.ForeignPtr
import Foreign.Ptr (Ptr)
import GHC.Records (HasField (..))
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.Expr
import Language.Halide.Type
import System.IO.Unsafe (unsafePerformIO)
import Prelude hiding (tail)

-- | Haskell counterpart of [@Halide::Internal::Dimension@](https://halide-lang.org/docs/class_halide_1_1_internal_1_1_dimension.html).
data CxxDimension

importHalide

-- | Information about a buffer's dimension, such as the min, extent, and stride.
newtype Dimension = Dimension (ForeignPtr CxxDimension)

instance Show Dimension where
  showsPrec d dim =
    showParen (d > 10) $
      showString "Dimension { min="
        . shows dim.min
        . showString (", extent=" :: String)
        . shows dim.extent
        . showString (", stride=" :: String)
        . shows dim.stride
        . showString " }"

instance HasField "min" Dimension (Expr Int32) where
  getField :: Dimension -> Expr Int32
  getField dim = unsafePerformIO $
    withCxxDimension dim $ \d ->
      cxxConstructExpr $ \ptr ->
        [CU.exp| void { new ($(Halide::Expr* ptr)) Halide::Expr{
          $(const Halide::Internal::Dimension* d)->min()} } |]

-- | Set the min in a given dimension to equal the given expression. Setting the mins to
-- zero may simplify some addressing math.
--
-- For more info, see [Halide::Internal::Dimension::set_min](https://halide-lang.org/docs/class_halide_1_1_internal_1_1_dimension.html#a84acaf7733391fdaea4f4cec24a60de2).
setMin :: Expr Int32 -> Dimension -> IO Dimension
setMin expr dim = do
  asExpr expr $ \n ->
    withCxxDimension dim $ \d ->
      [CU.exp| void {
        $(Halide::Internal::Dimension* d)->set_min(*$(const Halide::Expr* n)) } |]
  pure dim

instance HasField "extent" Dimension (Expr Int32) where
  getField :: Dimension -> Expr Int32
  getField dim = unsafePerformIO $
    withCxxDimension dim $ \d ->
      cxxConstructExpr $ \ptr ->
        [CU.exp| void { new ($(Halide::Expr* ptr)) Halide::Expr{
          $(const Halide::Internal::Dimension* d)->extent()} } |]

-- | Set the extent in a given dimension to equal the given expression.
--
-- Halide will generate runtime errors for Buffers that fail this check.
--
-- For more info, see [Halide::Internal::Dimension::set_extent](https://halide-lang.org/docs/class_halide_1_1_internal_1_1_dimension.html#a54111d8439a065bdaca5b9ff9bcbd630).
setExtent :: Expr Int32 -> Dimension -> IO Dimension
setExtent expr dim = do
  asExpr expr $ \n ->
    withCxxDimension dim $ \d ->
      [CU.exp| void {
        $(Halide::Internal::Dimension* d)->set_extent(*$(const Halide::Expr* n)) } |]
  pure dim

instance HasField "max" Dimension (Expr Int32) where
  getField :: Dimension -> Expr Int32
  getField dim = unsafePerformIO $
    withCxxDimension dim $ \d ->
      cxxConstructExpr $ \ptr ->
        [CU.exp| void { new ($(Halide::Expr* ptr)) Halide::Expr{
          $(Halide::Internal::Dimension* d)->max()} } |]

instance HasField "stride" Dimension (Expr Int32) where
  getField :: Dimension -> Expr Int32
  getField dim = unsafePerformIO $
    withCxxDimension dim $ \d ->
      cxxConstructExpr $ \ptr ->
        [CU.exp| void { new ($(Halide::Expr* ptr)) Halide::Expr{
          $(Halide::Internal::Dimension* d)->stride()} } |]

-- | Set the stride in a given dimension to equal the given expression.
--
-- This is particularly useful to set when vectorizing. Known strides for the vectorized
-- dimensions generate better code.
--
-- For more info, see [Halide::Internal::Dimension::set_stride](https://halide-lang.org/docs/class_halide_1_1_internal_1_1_dimension.html#a94f4c432a89907e2cc2aa908b5012cf8).
setStride :: Expr Int32 -> Dimension -> IO Dimension
setStride expr dim = do
  asExpr expr $ \n ->
    withCxxDimension dim $ \d ->
      [CU.exp| void {
        $(Halide::Internal::Dimension* d)->set_stride(*$(const Halide::Expr* n)) } |]
  pure dim

-- | Set estimates for autoschedulers.
setEstimate
  :: Expr Int32
  -- ^ @min@ estimate
  -> Expr Int32
  -- ^ @extent@ estimate
  -> Dimension
  -> IO Dimension
setEstimate minExpr extentExpr dim = do
  asExpr minExpr $ \m ->
    asExpr extentExpr $ \e ->
      withCxxDimension dim $ \d ->
        [CU.exp| void {
          $(Halide::Internal::Dimension* d)->set_estimate(*$(const Halide::Expr* m),
                                                          *$(const Halide::Expr* e)) } |]
  pure dim

wrapCxxDimension :: Ptr CxxDimension -> IO Dimension
wrapCxxDimension = fmap Dimension . newForeignPtr deleter
  where
    deleter = [C.funPtr| void deleteDimension(Halide::Internal::Dimension* p) { delete p; } |]

withCxxDimension :: Dimension -> (Ptr CxxDimension -> IO a) -> IO a
withCxxDimension (Dimension fp) = withForeignPtr fp