packages feed

accelerate-cuda-0.13.0.2: Data/Array/Accelerate/CUDA/Foreign.hs

{-# LANGUAGE CPP                  #-}
{-# LANGUAGE ConstraintKinds      #-}
{-# LANGUAGE DeriveDataTypeable   #-}
{-# LANGUAGE FlexibleContexts     #-}
{-# LANGUAGE FlexibleInstances    #-}
{-# LANGUAGE RankNTypes           #-}
{-# LANGUAGE ScopedTypeVariables  #-}
{-# LANGUAGE TypeFamilies         #-}
{-# LANGUAGE UndecidableInstances #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
-- |
-- Module      : Data.Array.Accelerate.CUDA.Foreign
-- Copyright   : [2013] Manuel M T Chakravarty, Gabriele Keller, Trevor L. McDonell, Robert Clifton-Everest
-- License     : BSD3
--
-- Maintainer  : Robert Clifton-Everest <robertce@cse.unsw.edu.au>
-- Stability   : experimental
-- Portability : non-portable (GHC extensions)
--
-- This module provides the CUDA backend's implementation of Accelerate's
-- foreign function interface. Also provided are a series of utility functions
-- for transferring arrays from the device to the host (and vice-versa),
-- allocating new arrays, getting the CUDA device pointers of a given array, and
-- executing IO actions within a CUDA context.
--
-- [/NOTE:/]
--
-- When arrays are passed to the foreign function there is no guarantee that the
-- host side data matches the device side data. If the data is needed host side
-- 'peekArray' or 'peekArrayAsync' must be called.
--
-- Arrays of tuples are represented as tuples of arrays so for example an array
-- of type @Array DIM1 (Float, Float)@ would have two device pointers associated
-- with it.
--

module Data.Array.Accelerate.CUDA.Foreign (

  -- * Backend representation
  cudaAcc, canExecute, CuForeignAcc, CuForeignExp, CIO,
  liftIO, canExecuteExp, cudaExp,

  -- * Manipulating arrays
  DevicePtrs,
  devicePtrsOfArray,
  indexArray, copyArray,
  useArray,  useArrayAsync,
  peekArray, peekArrayAsync,
  pokeArray, pokeArrayAsync,
  allocateArray, newArray,

  -- * Running IO actions in a CUDA context
  inContext, inDefaultContext

) where

import Data.Array.Accelerate.CUDA.State
import Data.Array.Accelerate.CUDA.Context
import Data.Array.Accelerate.CUDA.Array.Sugar
import Data.Array.Accelerate.CUDA.Array.Data
import Data.Array.Accelerate.CUDA.Array.Prim            ( DevicePtrs )

import Data.Dynamic
import Control.Applicative
import Control.Exception                                ( bracket_ )
import Control.Monad.Trans                              ( liftIO )
import System.IO.Unsafe                                 ( unsafePerformIO )
import System.Mem.StableName


-- CUDA backend representation of foreign functions
-- ------------------------------------------------

-- CUDA foreign Acc functions are just CIO functions.
--
newtype CuForeignAcc args results = CuForeignAcc (args -> CIO results)
  deriving (Typeable)

instance Foreign CuForeignAcc where
  -- Using the hash of the StableName in order to uniquely identify the function
  -- when it is pretty printed.
  --
  strForeign ff =
    let sn = unsafePerformIO $ makeStableName ff
    in
    "cudaAcc<" ++ (show (hashStableName sn)) ++ ">"

-- |Gives the executable form of a foreign function if it can be executed by the
-- CUDA backend.
--
canExecute :: forall ff args results. (Foreign ff, Typeable args, Typeable results)
           => ff args results
           -> Maybe (args -> CIO results)
canExecute ff =
  let
    df = toDyn ff
    fd = fromDynamic :: Dynamic -> Maybe (CuForeignAcc args results)
  in (\(CuForeignAcc ff') -> ff') <$> fd df

-- CUDA foreign Exp functions are just strings with the header filename and the name of the
-- function separated by a space.
--
newtype CuForeignExp args results = CuForeignExp String
  deriving (Typeable)

instance Foreign CuForeignExp where
  strForeign (CuForeignExp n) = "cudaExp<" ++ n ++ ">"

-- |Gives the foreign function name as a string if it is a foreign Exp function
-- for the CUDA backend.
--
canExecuteExp :: forall ff args results. (Foreign ff, Typeable results, Typeable args)
              => ff args results
              -> Maybe String
canExecuteExp ff =
  let
    df = toDyn ff
    fd = fromDynamic :: Dynamic -> Maybe (CuForeignExp args results)
  in (\(CuForeignExp ff') -> ff') <$> fd df


-- User facing utility functions
-- -----------------------------

-- |Create a CUDA foreign function
--
cudaAcc :: (Arrays args, Arrays results)
        => (args -> CIO results)
        -> CuForeignAcc args results
cudaAcc = CuForeignAcc

-- |Create a CUDA foreign scalar function. The string needs to be formatted in
-- the same way as for the Haskell FFI. That is, the header file name and the
-- name of the function separated by a space. i.e cudaExp "stdlib.h min".
--
cudaExp :: (Elt args, Elt results)
        => String
        -> CuForeignExp args results
cudaExp = CuForeignExp

-- |Get the raw CUDA device pointers associated with an array
--
devicePtrsOfArray :: Array sh e -> CIO (DevicePtrs (EltRepr e))
devicePtrsOfArray (Array _ adata) = devicePtrsOfArrayData adata

-- |Run an IO action within the given CUDA context
--
inContext :: Context -> IO a -> IO a
inContext ctx action =
  bracket_ (push ctx) pop action

-- |Run an IO action in the default CUDA context
--
inDefaultContext :: IO a -> IO a
inDefaultContext = inContext defaultContext