Copyright | (c) Tom Westerhout 2023 |
---|---|
Safe Haskell | Safe-Inferred |
Language | GHC2021 |
This package provides Haskell bindings that allow to write Halide embedded in Haskell without C++.
This module contains the reference documentation for Halide. If you're new, the best way to learn Halide is to have a look at the tutorials.
Synopsis
- data Expr a
- = Expr (ForeignPtr CxxExpr)
- | Var (ForeignPtr CxxVar)
- | RVar (ForeignPtr CxxRVar)
- | ScalarParam (IORef (Maybe (ForeignPtr CxxParameter)))
- type Var = Expr Int32
- type RVar = Expr Int32
- type VarOrRVar = Expr Int32
- class Storable a => IsHalideType a
- data ReductionDomain (n :: Nat)
- mkExpr :: IsHalideType a => a -> Expr a
- mkVar :: Text -> IO (Expr Int32)
- mkRVar :: Text -> Expr Int32 -> Expr Int32 -> IO (Expr Int32)
- mkRDom :: forall n. HasIndexType n => Text -> IndexType n -> IndexType n -> IO (ReductionDomain n)
- toRVars :: forall n. HasIndexType n => ReductionDomain n -> IO (IndexType n)
- undef :: forall a. IsHalideType a => Expr a
- cast :: forall to from. (IsHalideType to, IsHalideType from) => Expr from -> Expr to
- ifThenElse :: IsHalideType a => Expr Bool -> Expr a -> Expr a -> Expr a
- toIntImm :: IsHalideType a => Expr a -> Maybe Int
- printed :: forall a t. (IsHalideType a, PrintedType t (Expr a)) => Expr a -> t
- printedWhen :: forall a t. (IsHalideType a, PrintedType t (Expr a)) => Expr Bool -> Expr a -> t
- evaluate :: forall a. IsHalideType a => Expr a -> IO a
- eq :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- neq :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- lt :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- lte :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- gt :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- gte :: IsHalideType a => Expr a -> Expr a -> Expr Bool
- min :: IsHalideType a => Expr a -> Expr a -> Expr a
- max :: IsHalideType a => Expr a -> Expr a -> Expr a
- div :: forall a. (IsHalideType a, Integral a) => Expr a -> Expr a -> Expr a
- mod :: forall a. (IsHalideType a, Integral a) => Expr a -> Expr a -> Expr a
- and :: Expr Bool -> Expr Bool -> Expr Bool
- or :: Expr Bool -> Expr Bool -> Expr Bool
- data Func (t :: FuncTy) (n :: Nat) (a :: Type) where
- Func :: !(ForeignPtr CxxFunc) -> Func 'FuncTy n a
- Param :: IsHalideType a => !(IORef (Maybe (ForeignPtr CxxImageParam))) -> Func 'ParamTy n (Expr a)
- type Function n a = Func 'FuncTy n (Expr a)
- type Parameter n a = Func 'ParamTy n (Expr a)
- data FuncTy
- newtype Stage (n :: Nat) (a :: Type) = Stage (ForeignPtr CxxStage)
- define :: forall n d. (HasIndexType n, IsFuncDefinition d) => Text -> IndexType n -> d -> IO (Func 'FuncTy n d)
- update :: forall n d. (HasIndexType n, IsFuncDefinition d) => Func 'FuncTy n d -> IndexType n -> d -> IO ()
- (!) :: (HasIndexType n, IsFuncDefinition a) => Func t n a -> IndexType n -> a
- repeatEdge :: (KnownNat n, IsHalideType a) => Func 'ParamTy n (Expr a) -> IO (Func 'FuncTy n (Expr a))
- constantExterior :: (KnownNat n, IsHalideType a) => Expr a -> Func 'ParamTy n (Expr a) -> IO (Func 'FuncTy n (Expr a))
- getArgs :: KnownNat n => Func t n a -> IO [Var]
- hasUpdateDefinitions :: KnownNat n => Func t n a -> IO Bool
- getUpdateStage :: KnownNat n => Int -> Func 'FuncTy n a -> IO (Stage n a)
- newtype HalideBuffer (n :: Nat) (a :: Type) = HalideBuffer {}
- allocaCpuBuffer :: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) => [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- allocaBuffer :: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) => Target -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- class (KnownNat n, IsHalideType a, NestedList n a ~ b, NestedListLevel b ~ n, NestedListType b ~ a) => IsListPeek n a b | n a -> b, n b -> a, a b -> n where
- peekToList :: HasCallStack => Ptr (HalideBuffer n a) -> IO b
- peekScalar :: forall a. (HasCallStack, IsHalideType a) => Ptr (HalideBuffer 0 a) -> IO a
- class (KnownNat n, IsHalideType a) => IsHalideBuffer t n a where
- withHalideBufferImpl :: t -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- withHalideBuffer :: forall n a t b. IsHalideBuffer t n a => t -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- bufferFromPtrShapeStrides :: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) => Ptr a -> [Int] -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- bufferFromPtrShape :: (HasCallStack, KnownNat n, IsHalideType a) => Ptr a -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- realize :: forall n a t b. (KnownNat n, IsHalideType a) => Func t n (Expr a) -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- realizeOnTarget :: forall n a t b. (KnownNat n, IsHalideType a) => Target -> Func t n (Expr a) -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- asBufferParam :: forall n a t b. IsHalideBuffer t n a => t -> (Func 'ParamTy n (Expr a) -> IO b) -> IO b
- compile :: forall f n a. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) => f -> IO (LoweredSignature f)
- buffer :: forall n a. (KnownNat n, IsHalideType a) => Text -> Func 'ParamTy n (Expr a) -> Func 'ParamTy n (Expr a)
- scalar :: forall a. IsHalideType a => Text -> Expr a -> Expr a
- newtype Dimension = Dimension (ForeignPtr CxxDimension)
- dim :: forall n a. (HasCallStack, KnownNat n) => Int -> Func 'ParamTy n (Expr a) -> IO Dimension
- setMin :: Expr Int32 -> Dimension -> IO Dimension
- setExtent :: Expr Int32 -> Dimension -> IO Dimension
- setStride :: Expr Int32 -> Dimension -> IO Dimension
- setEstimate :: Expr Int32 -> Expr Int32 -> Dimension -> IO Dimension
- setScalarEstimate :: IsHalideType a => a -> Expr a -> IO ()
- newtype Target = Target (ForeignPtr CxxTarget)
- hostTarget :: Target
- gpuTarget :: Maybe Target
- compileForTarget :: forall f n a. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) => Target -> f -> IO (LoweredSignature f)
- data DeviceAPI
- data TargetFeature
- = FeatureJIT
- | FeatureDebug
- | FeatureNoAsserts
- | FeatureNoBoundsQuery
- | FeatureSSE41
- | FeatureAVX
- | FeatureAVX2
- | FeatureFMA
- | FeatureFMA4
- | FeatureF16C
- | FeatureARMv7s
- | FeatureNoNEON
- | FeatureVSX
- | FeaturePOWER_ARCH_2_07
- | FeatureCUDA
- | FeatureCUDACapability30
- | FeatureCUDACapability32
- | FeatureCUDACapability35
- | FeatureCUDACapability50
- | FeatureCUDACapability61
- | FeatureCUDACapability70
- | FeatureCUDACapability75
- | FeatureCUDACapability80
- | FeatureCUDACapability86
- | FeatureOpenCL
- | FeatureCLDoubles
- | FeatureCLHalf
- | FeatureCLAtomics64
- | FeatureOpenGLCompute
- | FeatureEGL
- | FeatureUserContext
- | FeatureProfile
- | FeatureNoRuntime
- | FeatureMetal
- | FeatureCPlusPlusMangling
- | FeatureLargeBuffers
- | FeatureHexagonDma
- | FeatureHVX_128
- | FeatureHVX_v62
- | FeatureHVX_v65
- | FeatureHVX_v66
- | FeatureFuzzFloatStores
- | FeatureSoftFloatABI
- | FeatureMSAN
- | FeatureAVX512
- | FeatureAVX512_KNL
- | FeatureAVX512_Skylake
- | FeatureAVX512_Cannonlake
- | FeatureAVX512_SapphireRapids
- | FeatureTraceLoads
- | FeatureTraceStores
- | FeatureTraceRealizations
- | FeatureTracePipeline
- | FeatureD3D12Compute
- | FeatureStrictFloat
- | FeatureTSAN
- | FeatureASAN
- | FeatureCheckUnsafePromises
- | FeatureEmbedBitcode
- | FeatureEnableLLVMLoopOpt
- | FeatureWasmSimd128
- | FeatureWasmSignExt
- | FeatureWasmSatFloatToInt
- | FeatureWasmThreads
- | FeatureWasmBulkMemory
- | FeatureSVE
- | FeatureSVE2
- | FeatureARMDotProd
- | FeatureARMFp16
- | FeatureRVV
- | FeatureARMv81a
- | FeatureSanitizerCoverage
- | FeatureProfileByTimer
- | FeatureSPIRV
- setFeature :: TargetFeature -> Target -> Target
- hasGpuFeature :: Target -> Bool
- hostSupportsTargetDevice :: Target -> Bool
- class KnownNat n => Schedulable f (n :: Nat) (a :: Type) where
- vectorize :: VarOrRVar -> f n a -> IO (f n a)
- unroll :: VarOrRVar -> f n a -> IO (f n a)
- reorder :: [VarOrRVar] -> f n a -> IO (f n a)
- split :: TailStrategy -> VarOrRVar -> (VarOrRVar, VarOrRVar) -> Expr Int32 -> f n a -> IO (f n a)
- fuse :: (VarOrRVar, VarOrRVar) -> VarOrRVar -> f n a -> IO (f n a)
- serial :: VarOrRVar -> f n a -> IO (f n a)
- parallel :: VarOrRVar -> f n a -> IO (f n a)
- atomic :: Bool -> f n a -> IO (f n a)
- specialize :: Expr Bool -> f n a -> IO (Stage n a)
- specializeFail :: Text -> f n a -> IO ()
- gpuBlocks :: (KnownNat k, 1 <= k, k <= 3) => DeviceAPI -> IndexType k -> f n a -> IO (f n a)
- gpuThreads :: (KnownNat k, 1 <= k, k <= 3) => DeviceAPI -> IndexType k -> f n a -> IO (f n a)
- gpuLanes :: DeviceAPI -> VarOrRVar -> f n a -> IO (f n a)
- computeWith :: LoopAlignStrategy -> f n a -> LoopLevel t -> IO ()
- data TailStrategy
- data LoopLevel (t :: LoopLevelTy) where
- data LoopLevelTy
- data LoopAlignStrategy
- computeRoot :: KnownNat n => Func t n a -> IO (Func t n a)
- getStage :: KnownNat n => Func t n a -> IO (Stage n a)
- getLoopLevel :: KnownNat n => Func t n a -> Expr Int32 -> IO (LoopLevel 'LockedTy)
- getLoopLevelAtStage :: KnownNat n => Func t n a -> Expr Int32 -> Int -> IO (LoopLevel 'LockedTy)
- asUsed :: KnownNat n => Func t n a -> IO (Func 'FuncTy n a)
- asUsedBy :: (KnownNat n, KnownNat m) => Func t1 n a -> Func 'FuncTy m b -> IO (Func 'FuncTy n a)
- copyToDevice :: KnownNat n => DeviceAPI -> Func t n a -> IO (Func t n a)
- copyToHost :: KnownNat n => Func t n a -> IO (Func t n a)
- storeAt :: KnownNat n => Func 'FuncTy n a -> LoopLevel t -> IO (Func 'FuncTy n a)
- computeAt :: KnownNat n => Func 'FuncTy n a -> LoopLevel t -> IO (Func 'FuncTy n a)
- estimate :: KnownNat n => Expr Int32 -> Expr Int32 -> Expr Int32 -> Func t n a -> IO ()
- bound :: KnownNat n => Expr Int32 -> Expr Int32 -> Expr Int32 -> Func t n a -> IO ()
- prettyLoopNest :: KnownNat n => Func t n r -> IO Text
- compileToLoweredStmt :: forall n a f. FuncBuilder f n a => StmtOutputFormat -> Target -> f -> IO Text
- data StmtOutputFormat
- data TraceEvent = TraceEvent {}
- data TraceEventCode
- data TraceLoadStoreContents = TraceLoadStoreContents {
- valuePtr :: !(Ptr ())
- valueType :: !HalideType
- coordinates :: ![Int]
- setCustomTrace :: KnownNat n => (TraceEvent -> IO ()) -> Func t n a -> IO b -> IO b
- traceStores :: KnownNat n => Func t n a -> IO (Func t n a)
- traceLoads :: KnownNat n => Func t n a -> IO (Func t n a)
- collectIterationOrder :: KnownNat n => (TraceEventCode -> Bool) -> Func t n a -> IO b -> IO ([[Int]], b)
- class (ToTuple a ~ t, FromTuple t ~ a) => IsTuple a t | a -> t, t -> a where
- type family ToTuple t = s | s -> t where ...
- type family FromTuple t = s | s -> t where ...
- type family Length (xs :: [k]) :: Nat where ...
- type family All (c :: Type -> Constraint) (ts :: [Type]) = (p :: Constraint) | p -> ts where ...
- compileToCallable :: forall n a f. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) => Target -> f -> IO (Callable (LoweredSignature f))
- testCUDA :: IO ()
- testOpenCL :: IO ()
- data SomeLoopLevel where
- SomeLoopLevel :: LoopLevel t -> SomeLoopLevel
- data RawHalideBuffer = RawHalideBuffer {
- halideBufferDevice :: !Word64
- halideBufferDeviceInterface :: !(Ptr HalideDeviceInterface)
- halideBufferHost :: !(Ptr Word8)
- halideBufferFlags :: !Word64
- halideBufferType :: !HalideType
- halideBufferDimensions :: !Int32
- halideBufferDim :: !(Ptr HalideDimension)
- halideBufferPadding :: !(Ptr ())
- data HalideDimension = HalideDimension {}
- data HalideDeviceInterface
- rowMajorStrides :: Integral a => [a] -> [a]
- colMajorStrides :: Integral a => [a] -> [a]
- isDeviceDirty :: Ptr RawHalideBuffer -> IO Bool
- isHostDirty :: Ptr RawHalideBuffer -> IO Bool
- getBufferExtent :: forall n a. KnownNat n => Ptr (HalideBuffer n a) -> Int -> IO Int
- bufferCopyToHost :: HasCallStack => Ptr RawHalideBuffer -> IO ()
- withCopiedToHost :: Ptr (HalideBuffer n a) -> IO b -> IO b
- withCropped :: Ptr (HalideBuffer n a) -> Int -> Int -> Int -> (Ptr (HalideBuffer n a) -> IO b) -> IO b
- data Dim = Dim {}
- data DimType
- data ForType
- data SplitContents = SplitContents {
- splitOld :: !Text
- splitOuter :: !Text
- splitInner :: !Text
- splitFactor :: !(Expr Int32)
- splitExact :: !Bool
- splitTail :: !TailStrategy
- data FuseContents = FuseContents {}
- data Split
- data Bound = Bound {}
- data StorageDim = StorageDim {
- storageVar :: !Text
- storageAlignment :: !(Maybe (Expr Int32))
- storageBound :: !(Maybe (Expr Int32))
- storageFold :: !(Maybe (Expr Int32, Bool))
- data FusedPair = FusedPair !Text !(Text, Int) !(Text, Int)
- data FuseLoopLevel = FuseLoopLevel !SomeLoopLevel
- data StageSchedule = StageSchedule {
- rvars :: ![ReductionVariable]
- splits :: ![Split]
- dims :: ![Dim]
- prefetches :: ![PrefetchDirective]
- fuseLevel :: !FuseLoopLevel
- fusedPairs :: ![FusedPair]
- allowRaceConditions :: !Bool
- atomic :: !Bool
- overrideAtomicAssociativityTest :: !Bool
- data ReductionVariable = ReductionVariable {}
- data PrefetchDirective = PrefetchDirective {
- prefetchFunc :: !Text
- prefetchAt :: !Text
- prefetchFrom :: !Text
- prefetchOffset :: !(Expr Int32)
- prefetchStrategy :: !PrefetchBoundStrategy
- prefetchParameter :: !(Maybe (ForeignPtr CxxParameter))
- getStageSchedule :: Stage n a -> IO StageSchedule
- data AutoScheduler
- loadAutoScheduler :: AutoScheduler -> IO ()
- applyAutoScheduler :: KnownNat n => AutoScheduler -> Target -> Func t n a -> IO Text
- getHalideLibraryPath :: IO (Maybe Text)
- applySplits :: KnownNat n => [Split] -> Stage n a -> IO ()
- applyDims :: KnownNat n => [Dim] -> Stage n a -> IO ()
- applySchedule :: KnownNat n => StageSchedule -> Stage n a -> IO ()
- type family FunctionArguments (f :: Type) :: [Type] where ...
- type family FunctionReturn (f :: Type) :: Type where ...
- class Curry (args :: [Type]) (r :: Type) (f :: Type) | args r -> f where
- curryG :: (Arguments args -> r) -> f
- class UnCurry (f :: Type) (args :: [Type]) (r :: Type) | args r -> f, args f -> r where
- uncurryG :: f -> Arguments args -> r
- type family LoweredSignature f where ...
- importHalide :: DecsQ
- testWriteToStderr :: IO ()
- data CxxExpr
- data CxxVar
- data CxxRVar
- data CxxParameter
- data CxxFunc
- data CxxImageParam
- data CxxStage
- data CxxDimension
- data CxxTarget
- data CxxLoopLevel
- data Int32
- data Ptr a
- class KnownNat (n :: Nat)
Scalar expressions
The basic building block of Halide pipelines is Expr
. Expr a
represents a scalar expression of
type a
, where a
must be an instance of IsHalideType
.
A scalar expression in Halide.
To have a nice experience writing arithmetic expressions in terms of Expr
s, we want to derive Num
,
Floating
etc. instances for Expr
. Unfortunately, that means that we encode Expr
, Var
, RVar
,
and ScalarParam
by the same type, and passing an Expr
to a function that expects a Var
will produce
a runtime error.
Expr (ForeignPtr CxxExpr) | Scalar expression. |
Var (ForeignPtr CxxVar) | Index variable. |
RVar (ForeignPtr CxxRVar) | Reduction variable. |
ScalarParam (IORef (Maybe (ForeignPtr CxxParameter))) | Scalar parameter. The |
Instances
class Storable a => IsHalideType a Source #
Specifies that a type is supported by Halide.
halideTypeFor, toCxxExpr
Instances
data ReductionDomain (n :: Nat) Source #
An n
-dimensional reduction domain.
Creating
mkExpr :: IsHalideType a => a -> Expr a Source #
Create a scalar expression from a Haskell value.
Create a named reduction variable.
For more information about reduction variables, see Halide::RDom
.
:: forall n. HasIndexType n | |
=> Text | name |
-> IndexType n | mins |
-> IndexType n | extents |
-> IO (ReductionDomain n) | reduction variables |
Create a reduction domain. Use asRVar
to cast it into an index.
For more information about reduction variables, see Halide::RDom
.
toRVars :: forall n. HasIndexType n => ReductionDomain n -> IO (IndexType n) Source #
Cast a reduction domain into a multi-dimensional index that can be used to perform multi-dimensional reductions.
undef :: forall a. IsHalideType a => Expr a Source #
Return an undef value of the given type.
For more information, see Halide::undef
.
cast :: forall to from. (IsHalideType to, IsHalideType from) => Expr from -> Expr to Source #
Cast a scalar expression to a different type.
Use TypeApplications with this function, e.g. cast @Float x
.
ifThenElse :: IsHalideType a => Expr Bool -> Expr a -> Expr a -> Expr a Source #
'ifThenElse cond a b' is the analogue of if cond then a else b
, but
lifted to work with Expr
types.
See also the RebindableSyntax extension.
Inspecting
toIntImm :: IsHalideType a => Expr a -> Maybe Int Source #
Convert expression to integer immediate.
Tries to extract the value of an expression if it is a compile-time constant. If the expression
isn't known at compile-time of the Halide pipeline, returns Nothing
.
printed :: forall a t. (IsHalideType a, PrintedType t (Expr a)) => Expr a -> t Source #
Print all expressions to stdout when the result is evaluates. The first expression is returned.
This is useful for debugging Halide pipelines.
This function is similar to printf
in that it accepts a variable number of arguments,
i.e the following is valid:
let x :: Expr Float x = 1 in printed (sin x) ("<- sin(" :: Text) x (")" :: Text)
:: Text
specifications are only needed if you have the OverloadedStrings
extension enabled.
printedWhen :: forall a t. (IsHalideType a, PrintedType t (Expr a)) => Expr Bool -> Expr a -> t Source #
evaluate :: forall a. IsHalideType a => Expr a -> IO a Source #
Evaluate a scalar expression.
It should contain no parameters. If it does contain parameters, an exception will be thrown.
Comparisons
We can't use Eq
and Ord
instances here, because we want the comparison to happen
when the pipeline is run rather than when it's built. Hence, we define lifted version of
various comparison operators. Note, that infix versions of the these functions have the
same precedence as the normal comparison operators.
div :: forall a. (IsHalideType a, Integral a) => Expr a -> Expr a -> Expr a Source #
Divide two integers, rounding towards zero.
mod :: forall a. (IsHalideType a, Integral a) => Expr a -> Expr a -> Expr a Source #
Compute the remainder of dividing two integers, when division is rounding toward zero.
Boolean functions
Functions
data Func (t :: FuncTy) (n :: Nat) (a :: Type) where Source #
A function in Halide. Conceptually, it can be thought of as a lazy
n
-dimensional buffer of type a
.
Here, a
is most often
for a type Expr
tt
that is an instance of IsHalideType
.
However, one can also define Func
s that return multiple values. In this case, a
will
be a tuple of Expr
s.
This is a wrapper around the Halide::Func
C++ type.
Func :: !(ForeignPtr CxxFunc) -> Func 'FuncTy n a | |
Param :: IsHalideType a => !(IORef (Maybe (ForeignPtr CxxImageParam))) -> Func 'ParamTy n (Expr a) |
Instances
type Function n a = Func 'FuncTy n (Expr a) Source #
Synonym for the most commonly used function type.
type Parameter n a = Func 'ParamTy n (Expr a) Source #
Synonym for the most commonly used parameter type.
Function type. It can either be FuncTy
which means that we have defined the function ourselves,
or ParamTy
which means that it's a parameter to our pipeline.
newtype Stage (n :: Nat) (a :: Type) Source #
A single definition of a Func
.
Instances
Creating
define :: forall n d. (HasIndexType n, IsFuncDefinition d) => Text -> IndexType n -> d -> IO (Func 'FuncTy n d) Source #
Define a Halide function.
define "f" i e
defines a Halide function called "f" such that f[i] = e
.
Here, i
is an n
-element tuple of Var
, i.e. the following are all valid:
>>>
[x, y, z] <- mapM mkVar ["x", "y", "z"]
>>>
f1 <- define "f1" x (0 :: Expr Float)
>>>
f2 <- define "f2" (x, y) (0 :: Expr Float)
>>>
f3 <- define "f3" (x, y, z) (0 :: Expr Float)
update :: forall n d. (HasIndexType n, IsFuncDefinition d) => Func 'FuncTy n d -> IndexType n -> d -> IO () Source #
Create an update definition for a Halide function.
update f i e
creates an update definition for f
that performs f[i] = e
.
(!) :: (HasIndexType n, IsFuncDefinition a) => Func t n a -> IndexType n -> a infix 9 Source #
Apply a Halide function. Conceptually, f ! i
is equivalent to f[i]
, i.e.
indexing into a lazy array.
repeatEdge :: (KnownNat n, IsHalideType a) => Func 'ParamTy n (Expr a) -> IO (Func 'FuncTy n (Expr a)) Source #
Impose a boundary condition such that the nearest edge sample is returned everywhere outside the given region.
For more information, see Halide::repeat_edge
.
constantExterior :: (KnownNat n, IsHalideType a) => Expr a -> Func 'ParamTy n (Expr a) -> IO (Func 'FuncTy n (Expr a)) Source #
Impose a boundary condition such that a given expression is returned everywhere outside the boundary.
For more information, see Halide::constant_exterior
.
Inspecting
getArgs :: KnownNat n => Func t n a -> IO [Var] Source #
Get the index arguments of the function.
The returned list contains exactly n
elements.
getUpdateStage :: KnownNat n => Int -> Func 'FuncTy n a -> IO (Stage n a) Source #
Get a handle to an update step for the purposes of scheduling it.
Buffers
In the C interface of Halide, buffers are described by the C struct
halide_buffer_t
. On the Haskell side,
we have HalideBuffer
.
newtype HalideBuffer (n :: Nat) (a :: Type) Source #
An n
-dimensional buffer of elements of type a
.
Most pipelines use
for input and output array arguments.Ptr
(HalideBuffer
n a)
Instances
Show (HalideBuffer n a) Source # | |
Defined in Language.Halide.Buffer | |
Eq (HalideBuffer n a) Source # | |
Defined in Language.Halide.Buffer (==) :: HalideBuffer n a -> HalideBuffer n a -> Bool Source # (/=) :: HalideBuffer n a -> HalideBuffer n a -> Bool Source # |
To easily test out your pipeline, there are helper functions to create HalideBuffer
s without
worrying about the low-level representation.
allocaCpuBuffer :: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) => [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b Source #
Temporary allocate a CPU buffer.
This is useful for testing and debugging when you need to allocate an output buffer for your pipeline. E.g.
allocaCpuBuffer
[3, 3] $ out -> do myKernel out -- fill the buffer print =<<peekToList
out -- print it for debugging
allocaBuffer :: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) => Target -> [Int] -> (Ptr (HalideBuffer n a) -> IO b) -> IO b Source #
Buffers can also be converted to lists to easily print them for debugging.
class (KnownNat n, IsHalideType a, NestedList n a ~ b, NestedListLevel b ~ n, NestedListType b ~ a) => IsListPeek n a b | n a -> b, n b -> a, a b -> n where Source #
peekToList :: HasCallStack => Ptr (HalideBuffer n a) -> IO b Source #
Instances
(IsHalideType a, NestedListLevel [a] ~ 1, NestedListType [a] ~ a) => IsListPeek 1 a [a] Source # | |
Defined in Language.Halide.Buffer peekToList :: Ptr (HalideBuffer 1 a) -> IO [a] Source # | |
(IsHalideType a, NestedListLevel [[a]] ~ 2, NestedListType [[a]] ~ a) => IsListPeek 2 a [[a]] Source # | |
Defined in Language.Halide.Buffer peekToList :: Ptr (HalideBuffer 2 a) -> IO [[a]] Source # | |
(IsHalideType a, NestedListLevel [[[a]]] ~ 3, NestedListType [[[a]]] ~ a) => IsListPeek 3 a [[[a]]] Source # | |
Defined in Language.Halide.Buffer peekToList :: Ptr (HalideBuffer 3 a) -> IO [[[a]]] Source # | |
(IsHalideType a, NestedListLevel [[[[a]]]] ~ 4, NestedListType [[[[a]]]] ~ a) => IsListPeek 4 a [[[[a]]]] Source # | |
Defined in Language.Halide.Buffer peekToList :: Ptr (HalideBuffer 4 a) -> IO [[[[a]]]] Source # |
peekScalar :: forall a. (HasCallStack, IsHalideType a) => Ptr (HalideBuffer 0 a) -> IO a Source #
For production usage however, you don't want to work with lists. Instead, you probably want Halide
to work with your existing array data types. For this, we define IsHalideBuffer
typeclass that
teaches Halide how to convert your data into a HalideBuffer
. Depending on how you implement the
instance, this can be very efficient, because it need not involve any memory copying.
class (KnownNat n, IsHalideType a) => IsHalideBuffer t n a where Source #
Specifies that a type t
can be used as an n
-dimensional Halide buffer with elements of type a
.
withHalideBufferImpl :: t -> (Ptr (HalideBuffer n a) -> IO b) -> IO b Source #
Instances
IsHalideType a => IsHalideBuffer (Vector a) 1 a Source # | Storable vectors are one-dimensional buffers. This involves no copying. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: Vector a -> (Ptr (HalideBuffer 1 a) -> IO b) -> IO b Source # | |
IsHalideType a => IsHalideBuffer [[[[a]]]] 4 a Source # | Lists can also act as Halide buffers. Use for testing only. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: [[[[a]]]] -> (Ptr (HalideBuffer 4 a) -> IO b) -> IO b Source # | |
IsHalideType a => IsHalideBuffer [[[a]]] 3 a Source # | Lists can also act as Halide buffers. Use for testing only. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: [[[a]]] -> (Ptr (HalideBuffer 3 a) -> IO b) -> IO b Source # | |
IsHalideType a => IsHalideBuffer [[a]] 2 a Source # | Lists can also act as Halide buffers. Use for testing only. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: [[a]] -> (Ptr (HalideBuffer 2 a) -> IO b) -> IO b Source # | |
IsHalideType a => IsHalideBuffer [a] 1 a Source # | Lists can also act as Halide buffers. Use for testing only. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: [a] -> (Ptr (HalideBuffer 1 a) -> IO b) -> IO b Source # | |
IsHalideType a => IsHalideBuffer (MVector RealWorld a) 1 a Source # | Storable vectors are one-dimensional buffers. This involves no copying. |
Defined in Language.Halide.Buffer withHalideBufferImpl :: MVector RealWorld a -> (Ptr (HalideBuffer 1 a) -> IO b) -> IO b Source # |
withHalideBuffer :: forall n a t b. IsHalideBuffer t n a => t -> (Ptr (HalideBuffer n a) -> IO b) -> IO b Source #
Treat a type t
as a HalideBuffer
and use it in an IO
action.
This function is a simple wrapper around withHalideBufferImpl
, except that the order of type parameters
is reversed. If you have TypeApplications
extension enabled, this allows you to write
withHalideBuffer
3 Float yourBuffer
to specify that you want a 3-dimensional buffer of Float
.
There are also helper functions to simplify writing instances of IsHalideBuffer
.
bufferFromPtrShapeStrides Source #
:: forall n a b. (HasCallStack, KnownNat n, IsHalideType a) | |
=> Ptr a | CPU pointer to the data |
-> [Int] | Extents (in number of elements, not in bytes) |
-> [Int] | Strides (in number of elements, not in bytes) |
-> (Ptr (HalideBuffer n a) -> IO b) | Action to run |
-> IO b |
Construct a HalideBuffer
from a pointer to the data, a list of extents,
and a list of strides, and use it in an IO
action.
This function throws a runtime error if the number of dimensions does not
match n
.
:: (HasCallStack, KnownNat n, IsHalideType a) | |
=> Ptr a | CPU pointer to the data |
-> [Int] | Extents (in number of elements, not in bytes) |
-> (Ptr (HalideBuffer n a) -> IO b) | |
-> IO b |
Similar to bufferFromPtrShapeStrides
, but assumes column-major ordering of data.
Running the pipelines
There are a few ways how one can run a Halide pipeline.
The simplest way to build a Func
and then call realize
to evaluate it over a rectangular domain.
:: forall n a t b. (KnownNat n, IsHalideType a) | |
=> Func t n (Expr a) | Function to evaluate |
-> [Int] | Domain over which to evaluate |
-> (Ptr (HalideBuffer n a) -> IO b) | What to do with the buffer afterwards. Note that the buffer is allocated only temporary, so do not return it directly. |
-> IO b |
Similar to realizeOnTarget
except that the pipeline is run on hostTarget
.
:: forall n a t b. (KnownNat n, IsHalideType a) | |
=> Target | Target on which to run the pipeline |
-> Func t n (Expr a) | Function to evaluate |
-> [Int] | Domain over which to evaluate |
-> (Ptr (HalideBuffer n a) -> IO b) | What to do with the buffer afterwards. Note that the buffer is allocated only temporary, so do not return it directly. |
-> IO b |
Evaluate this function over a rectangular domain.
If your target is a GPU, this function will not automatically copy data back from the GPU.
:: forall n a t b. IsHalideBuffer t n a | |
=> t | Object to treat as a buffer |
-> (Func 'ParamTy n (Expr a) -> IO b) | What to do with the temporary buffer |
-> IO b |
Wrap a buffer into a Func
.
Suppose, we are defining a pipeline that adds together two vectors, and we'd like to call realize
to
evaluate it directly, how do we pass the vectors to the Func
? asBufferParam
allows to do exactly this.
asBuffer [1, 2, 3] $ \a -> asBuffer [4, 5, 6] $ \b -> do i <- mkVar "i" f <- define "vectorAdd" i $ a ! i + b ! i realize f [3] $ \result -> print =<< peekToList f
The drawback of calling realize
all the time is that it's impossible to pass parameters to pipelines.
We can define pipelines that operate on buffers using asBufferParam
, but we have to recompile the
pipeline for every new buffer.
A better way to handle pipeline parameters is to define a Haskell function that accepts Expr
s
and Func
s as arguments and returns a Func
. We can then pass this function to compile
(or compileForTarget
), and it compile it into a Haskell function that can now be invoked with
normal scalars instead of Expr
s and Ptr
s instead of HalideBuffer
Func
s.
:: forall f n a. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) | |
=> f | Function to compile |
-> IO (LoweredSignature f) | Compiled kernel |
Specifies how Expr
and Func
parameters become scalar and buffer arguments in compiled kernels.
type family Lowered (t :: k) :: k where
Lowered (Expr a) = a
Lowered (Func t n (Expr a)) = Ptr (HalideBuffer n a)
Lowered '[] = '[]
Lowered (t ': ts) = (Lowered t ': Lowered ts)
A constraint that specifies that the function f
returns
.
class (FunctionReturn f ~ IO (Func 'FuncTy n a), KnownNat n) => ReturnsFunc f n a | f -> n aIO
(Func
t n a)
Convert a function that builds a Halide Func
into a normal Haskell function acccepting scalars and
HalideBuffer
s.
For example:
builder :: Expr Float -> Func 'ParamTy 1 Float -> IO (Func 'FuncTy 1 Float) builder scale inputVector = do i <-mkVar
"i" scaledVector <-define
"scaledVector" i $ scale * inputVector!
i pure scaledVector
The builder
function accepts a scalar parameter and a vector and scales the vector by the given factor.
We can now pass builder
to compile
:
scaler <-compile
builderwithHalideBuffer
1
Float [1, 1, 1] $ inputVector ->allocaCpuBuffer
[3] $ outputVector -> do -- invoke the kernel scaler 2.0 inputVector outputVector -- print the result print =<<peekToList
outputVector
Parameters
Similar to how we can specify the name of a variable in mkVar
(or mkRVar
) or function in define
,
one can also specify the name of a pipeline parameter. This is achieved by using the ViewPatterns
extension together with the scalar
and buffer
helper functions.
buffer :: forall n a. (KnownNat n, IsHalideType a) => Text -> Func 'ParamTy n (Expr a) -> Func 'ParamTy n (Expr a) Source #
A view pattern to specify the name of a buffer argument.
Example usage:
>>>
:{
_ <- compile $ \(buffer "src" -> src) -> do i <- mkVar "i" define "dest" i $ (src ! i :: Expr Float) :}
or if we want to specify the dimension and type, we can use type applications:
>>>
:{
_ <- compile $ \(buffer @1 @Float "src" -> src) -> do i <- mkVar "i" define "dest" i $ src ! i :}
scalar :: forall a. IsHalideType a => Text -> Expr a -> Expr a Source #
Similar to buffer
, but for scalar parameters.
Example usage:
>>>
:{
_ <- compile $ \(scalar @Float "a" -> a) -> do i <- mkVar "i" define "dest" i $ a :}
Another common thing to do with the parameters is to explicitly specify their shapes. For this, we expose the Dimension
type:
Information about a buffer's dimension, such as the min, extent, and stride.
dim :: forall n a. (HasCallStack, KnownNat n) => Int -> Func 'ParamTy n (Expr a) -> IO Dimension Source #
Get a particular dimension of a pipeline parameter.
setMin :: Expr Int32 -> Dimension -> IO Dimension Source #
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.
setExtent :: Expr Int32 -> Dimension -> IO Dimension Source #
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.
setStride :: Expr Int32 -> Dimension -> IO Dimension Source #
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.
Set estimates for autoschedulers.
:: IsHalideType a | |
=> a | Estimate |
-> Expr a | Parameter |
-> IO () |
Targets
The compilation target.
This is the Haskell counterpart of Halide::Target
.
hostTarget :: Target Source #
Return the target that Halide will use by default.
If the HL_TARGET
environment variable is set, it uses that. Otherwise, it
returns the target corresponding to the host machine.
gpuTarget :: Maybe Target Source #
Get the default GPU target. We first check for CUDA and then for OpenCL.
If neither of the two is usable, Nothing
is returned.
compileForTarget :: forall f n a. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) => Target -> f -> IO (LoweredSignature f) Source #
Similar to compile
, but the first argument lets you explicitly specify the compilation target.
An enum describing the type of device API.
This is the Haskell counterpart of Halide::DeviceAPI
.
DeviceNone | |
DeviceHost | |
DeviceDefaultGPU | |
DeviceCUDA | |
DeviceOpenCL | |
DeviceOpenGLCompute | |
DeviceMetal | |
DeviceHexagon | |
DeviceHexagonDma | |
DeviceD3D12Compute |
Instances
Enum DeviceAPI Source # | |
Defined in Language.Halide.Target succ :: DeviceAPI -> DeviceAPI Source # pred :: DeviceAPI -> DeviceAPI Source # toEnum :: Int -> DeviceAPI Source # fromEnum :: DeviceAPI -> Int Source # enumFrom :: DeviceAPI -> [DeviceAPI] Source # enumFromThen :: DeviceAPI -> DeviceAPI -> [DeviceAPI] Source # enumFromTo :: DeviceAPI -> DeviceAPI -> [DeviceAPI] Source # enumFromThenTo :: DeviceAPI -> DeviceAPI -> DeviceAPI -> [DeviceAPI] Source # | |
Show DeviceAPI Source # | |
Eq DeviceAPI Source # | |
Ord DeviceAPI Source # | |
Defined in Language.Halide.Target |
data TargetFeature Source #
Note: generated automatically using
cat $HALIDE_PATH/include/Halide.h | \ grep -E '.* = halide_target_feature_.*' | \ sed -E 's/^\s*(.*) = .*$/ | \1/g' | \ grep -v FeatureEnd
Instances
:: TargetFeature | Feature to add |
-> Target | Initial target |
-> Target | New target |
Add a feature to target.
hasGpuFeature :: Target -> Bool Source #
Return whether a GPU compute runtime is enabled.
Checks whether gpuBlocks
and similar are going to work.
For more info, see Target::has_gpu_feature
.
hostSupportsTargetDevice Source #
Attempt to sniff whether a given Target
(and its implied DeviceAPI
) is usable on the
current host.
Note that a return value of True
does not guarantee that future usage of that device will
succeed; it is intended mainly as a simple diagnostic to allow early-exit when a desired device
is definitely not usable.
Also note that this call is NOT threadsafe, as it temporarily redirects various global error-handling hooks in Halide.
Scheduling
class KnownNat n => Schedulable f (n :: Nat) (a :: Type) where Source #
Common scheduling functions
vectorize :: VarOrRVar -> f n a -> IO (f n a) Source #
Vectorize the dimension.
unroll :: VarOrRVar -> f n a -> IO (f n a) Source #
Unroll the dimension.
Reorder variables to have the given nesting order, from innermost out.
Note that variables
should only contain variables that belong to the function.
If this is not the case, a runtime error will be thrown.
split :: TailStrategy -> VarOrRVar -> (VarOrRVar, VarOrRVar) -> Expr Int32 -> f n a -> IO (f n a) Source #
Split a dimension into inner and outer subdimensions with the given names, where the inner dimension
iterates from 0
to factor-1
.
The inner and outer subdimensions can then be dealt with using the other scheduling calls. It's okay to reuse the old variable name as either the inner or outer variable. The first argument specifies how the tail should be handled if the split factor does not provably divide the extent.
fuse :: (VarOrRVar, VarOrRVar) -> VarOrRVar -> f n a -> IO (f n a) Source #
Join two dimensions into a single fused dimenion.
The fused dimension covers the product of the extents of the inner and outer dimensions given.
serial :: VarOrRVar -> f n a -> IO (f n a) Source #
Mark the dimension to be traversed serially
parallel :: VarOrRVar -> f n a -> IO (f n a) Source #
Mark the dimension to be traversed in parallel
Issue atomic updates for this Func.
specialize :: Expr Bool -> f n a -> IO (Stage n a) Source #
specializeFail :: Text -> f n a -> IO () Source #
gpuBlocks :: (KnownNat k, 1 <= k, k <= 3) => DeviceAPI -> IndexType k -> f n a -> IO (f n a) Source #
gpuThreads :: (KnownNat k, 1 <= k, k <= 3) => DeviceAPI -> IndexType k -> f n a -> IO (f n a) Source #
gpuLanes :: DeviceAPI -> VarOrRVar -> f n a -> IO (f n a) Source #
computeWith :: LoopAlignStrategy -> f n a -> LoopLevel t -> IO () Source #
Schedule the iteration over this stage to be fused with another stage from outermost loop to a given LoopLevel.
For more info, see Halide::Stage::compute_with.
Instances
data TailStrategy Source #
Different ways to handle a tail case in a split when the split factor does not provably divide the extent.
This is the Haskell counterpart of Halide::TailStrategy
.
TailRoundUp | Round up the extent to be a multiple of the split factor. Not legal for RVars, as it would change the meaning of the algorithm.
|
TailGuardWithIf | Guard the inner loop with an if statement that prevents evaluation beyond the original extent. Always legal. The if statement is treated like a boundary condition, and factored out into a loop epilogue if possible.
|
TailPredicate | Guard the loads and stores in the loop with an if statement that prevents evaluation beyond the original extent. Always legal. The if statement is treated like a boundary condition, and factored out into a loop epilogue if possible. * Pros: no redundant re-evaluation; does not constrain input or output sizes. * Cons: increases code size due to separate tail-case handling. |
TailPredicateLoads | Guard the loads in the loop with an if statement that prevents evaluation beyond the original extent. Only legal for innermost splits. Not legal for RVars, as it would change the meaning of the algorithm. The if statement is treated like a boundary condition, and factored out into a loop epilogue if possible. * Pros: does not constrain input sizes, output size constraints are simpler than full predication. * Cons: increases code size due to separate tail-case handling, constrains the output size to be a multiple of the split factor. |
TailPredicateStores | Guard the stores in the loop with an if statement that prevents evaluation beyond the original extent. Only legal for innermost splits. Not legal for RVars, as it would change the meaning of the algorithm. The if statement is treated like a boundary condition, and factored out into a loop epilogue if possible. * Pros: does not constrain output sizes, input size constraints are simpler than full predication. * Cons: increases code size due to separate tail-case handling, constraints the input size to be a multiple of the split factor. |
TailShiftInwards | Prevent evaluation beyond the original extent by shifting the tail case inwards, re-evaluating some points near the end. Only legal for pure variables in pure definitions. If the inner loop is very simple, the tail case is treated like a boundary condition and factored out into an epilogue. This is a good trade-off between several factors. Like |
TailAuto | For pure definitions use For pure vars in update definitions use |
Instances
data LoopLevel (t :: LoopLevelTy) where Source #
A reference to a site in a Halide statement at the top of the body of a particular for loop.
InlinedLoopLevel :: LoopLevel 'InlinedTy | |
RootLoopLevel :: LoopLevel 'RootTy | |
LoopLevel :: !(ForeignPtr CxxLoopLevel) -> LoopLevel 'LockedTy |
data LoopAlignStrategy Source #
Different ways to handle the case when the start/end of the loops of stages computed with (fused) are not aligned.
LoopAlignStart | Shift the start of the fused loops to align. |
LoopAlignEnd | Shift the end of the fused loops to align. |
LoopNoAlign |
|
LoopAlignAuto | By default, LoopAlignStrategy is set to |
Instances
computeRoot :: KnownNat n => Func t n a -> IO (Func t n a) Source #
Compute all of this function once ahead of time.
See Halide::Func::compute_root for more info.
getStage :: KnownNat n => Func t n a -> IO (Stage n a) Source #
Get the pure stage of a Func
for the purposes of scheduling it.
getLoopLevel :: KnownNat n => Func t n a -> Expr Int32 -> IO (LoopLevel 'LockedTy) Source #
Same as getLoopLevelAtStage
except that the stage is -1
.
Identify the loop nest corresponding to some dimension of some function.
asUsed :: KnownNat n => Func t n a -> IO (Func 'FuncTy n a) Source #
Create and return a global identity wrapper, which wraps all calls to this Func by any other Func.
If a global wrapper already exists, returns it. The global identity wrapper is only used by callers for which no custom wrapper has been specified.
asUsedBy :: (KnownNat n, KnownNat m) => Func t1 n a -> Func 'FuncTy m b -> IO (Func 'FuncTy n a) Source #
Creates and returns a new identity Func that wraps this Func.
During compilation, Halide replaces all calls to this Func done by f
with calls to the wrapper.
If this Func is already wrapped for use in f
, will return the existing wrapper.
For more info, see Halide::Func::in.
copyToDevice :: KnownNat n => DeviceAPI -> Func t n a -> IO (Func t n a) Source #
Declare that this function should be implemented by a call to halide_buffer_copy
with the given
target device API.
Asserts that the Func
has a pure definition which is a simple call to a single input, and no update
definitions. The wrapper Func
s returned by asUsed
are suitable candidates. Consumes all pure variables,
and rewrites the Func
to have an extern definition that calls halide_buffer_copy
.
copyToHost :: KnownNat n => Func t n a -> IO (Func t n a) Source #
Same as copyToDevice
DeviceHost
storeAt :: KnownNat n => Func 'FuncTy n a -> LoopLevel t -> IO (Func 'FuncTy n a) Source #
Allocate storage for this function within a particular loop level.
Scheduling storage is optional, and can be used to separate the loop level at which storage is allocated from the loop level at which computation occurs to trade off between locality and redundant work.
For more info, see Halide::Func::store_at.
computeAt :: KnownNat n => Func 'FuncTy n a -> LoopLevel t -> IO (Func 'FuncTy n a) Source #
Schedule a function to be computed within the iteration over a given loop level.
For more info, see Halide::Func::compute_at.
:: KnownNat n | |
=> Expr Int32 | index variable |
-> Expr Int32 |
|
-> Expr Int32 |
|
-> Func t n a | |
-> IO () |
Statically declare the range over which the function will be evaluated in the general case.
This provides a basis for the auto scheduler to make trade-offs and scheduling decisions. The auto generated schedules might break when the sizes of the dimensions are very different from the estimates specified. These estimates are used only by the auto scheduler if the function is a pipeline output.
:: KnownNat n | |
=> Expr Int32 | index variable |
-> Expr Int32 |
|
-> Expr Int32 |
|
-> Func t n a | |
-> IO () |
Statically declare the range over which a function should be evaluated.
This can let Halide perform some optimizations. E.g. if you know there are going to be 4 color channels, you can completely vectorize the color channel dimension without the overhead of splitting it up. If bounds inference decides that it requires more of this function than the bounds you have stated, a runtime error will occur when you try to run your pipeline.
Debugging / Tracing
For debugging, it's often useful to observe the value of an expression when it's evaluated. If you
have a complex expression that does not depend on any buffers or indices, you can evaluate
it.
| However, often an expression is only used within a definition of a pipeline, and it's impossible to
call evaluate
on it. In such cases, it can be wrapped with printed
to indicate to Halide that the
value of the expression should be dumped to screen when it's computed.
prettyLoopNest :: KnownNat n => Func t n r -> IO Text Source #
Get the loop nests specified by the schedule for this function.
Helpful for understanding what a schedule is doing.
For more info, see
Halide::Func::print_loop_nest
compileToLoweredStmt :: forall n a f. FuncBuilder f n a => StmtOutputFormat -> Target -> f -> IO Text Source #
Get the internal representation of lowered code.
Useful for analyzing and debugging scheduling. Can emit HTML or plain text.
data StmtOutputFormat Source #
Format in which to return the lowered code.
Instances
Enum StmtOutputFormat Source # | |
Defined in Language.Halide.Kernel succ :: StmtOutputFormat -> StmtOutputFormat Source # pred :: StmtOutputFormat -> StmtOutputFormat Source # toEnum :: Int -> StmtOutputFormat Source # fromEnum :: StmtOutputFormat -> Int Source # enumFrom :: StmtOutputFormat -> [StmtOutputFormat] Source # enumFromThen :: StmtOutputFormat -> StmtOutputFormat -> [StmtOutputFormat] Source # enumFromTo :: StmtOutputFormat -> StmtOutputFormat -> [StmtOutputFormat] Source # enumFromThenTo :: StmtOutputFormat -> StmtOutputFormat -> StmtOutputFormat -> [StmtOutputFormat] Source # | |
Show StmtOutputFormat Source # | |
Defined in Language.Halide.Kernel | |
Eq StmtOutputFormat Source # | |
Defined in Language.Halide.Kernel (==) :: StmtOutputFormat -> StmtOutputFormat -> Bool Source # (/=) :: StmtOutputFormat -> StmtOutputFormat -> Bool Source # |
data TraceEvent Source #
TraceEvent | |
|
Instances
Show TraceEvent Source # | |
Defined in Language.Halide.Trace |
data TraceEventCode Source #
Haskell counterpart of halide_trace_event_code_t
.
TraceLoad | |
TraceStore | |
TraceBeginRealization | |
TraceEndRealization | |
TraceProduce | |
TraceEndProduce | |
TraceConsume | |
TraceEndConsume | |
TraceBeginPipeline | |
TraceEndPipeline | |
TraceTag |
Instances
data TraceLoadStoreContents Source #
TraceLoadStoreContents | |
|
Instances
Show TraceLoadStoreContents Source # | |
Defined in Language.Halide.Trace |
collectIterationOrder :: KnownNat n => (TraceEventCode -> Bool) -> Func t n a -> IO b -> IO ([[Int]], b) Source #
Type helpers
class (ToTuple a ~ t, FromTuple t ~ a) => IsTuple a t | a -> t, t -> a where Source #
Specifies that there is an isomorphism between a type a
and a tuple t
.
We use this class to convert between Arguments
and normal tuples.
Instances
IsTuple ('[] :: [Type]) () Source # | |
IsTuple '[Expr a1] (Expr a1) Source # | |
IsTuple '[a1, a2] (a1, a2) Source # | |
IsTuple '[a1, a2, a3] (a1, a2, a3) Source # | |
IsTuple '[a1, a2, a3, a4] (a1, a2, a3, a4) Source # | |
IsTuple '[a1, a2, a3, a4, a5] (a1, a2, a3, a4, a5) Source # | |
IsTuple '[a1, a2, a3, a4, a5, a6] (a1, a2, a3, a4, a5, a6) Source # | |
IsTuple '[a1, a2, a3, a4, a5, a6, a7] (a1, a2, a3, a4, a5, a6, a7) Source # | |
IsTuple '[a1, a2, a3, a4, a5, a6, a7, a8] (a1, a2, a3, a4, a5, a6, a7, a8) Source # | |
IsTuple '[a1, a2, a3, a4, a5, a6, a7, a8, a9] (a1, a2, a3, a4, a5, a6, a7, a8, a9) Source # | |
IsTuple '[a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) Source # | |
type family ToTuple t = s | s -> t where ... Source #
Type family that maps
to the corresponding tuple type.Arguments
ts
ToTuple '[] = () | |
ToTuple '[Expr a1] = Expr a1 | |
ToTuple '[a1, a2] = (a1, a2) | |
ToTuple '[a1, a2, a3] = (a1, a2, a3) | |
ToTuple '[a1, a2, a3, a4] = (a1, a2, a3, a4) | |
ToTuple '[a1, a2, a3, a4, a5] = (a1, a2, a3, a4, a5) | |
ToTuple '[a1, a2, a3, a4, a5, a6] = (a1, a2, a3, a4, a5, a6) | |
ToTuple '[a1, a2, a3, a4, a5, a6, a7] = (a1, a2, a3, a4, a5, a6, a7) | |
ToTuple '[a1, a2, a3, a4, a5, a6, a7, a8] = (a1, a2, a3, a4, a5, a6, a7, a8) | |
ToTuple '[a1, a2, a3, a4, a5, a6, a7, a8, a9] = (a1, a2, a3, a4, a5, a6, a7, a8, a9) | |
ToTuple '[a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] = (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) |
type family FromTuple t = s | s -> t where ... Source #
Type family that maps tuples to the corresponding
type. This is essentially the inverse
of Arguments
tsToTuple
.
FromTuple () = '[] | |
FromTuple (Expr a1) = '[Expr a1] | |
FromTuple (a1, a2) = '[a1, a2] | |
FromTuple (a1, a2, a3) = '[a1, a2, a3] | |
FromTuple (a1, a2, a3, a4) = '[a1, a2, a3, a4] | |
FromTuple (a1, a2, a3, a4, a5) = '[a1, a2, a3, a4, a5] | |
FromTuple (a1, a2, a3, a4, a5, a6) = '[a1, a2, a3, a4, a5, a6] | |
FromTuple (a1, a2, a3, a4, a5, a6, a7) = '[a1, a2, a3, a4, a5, a6, a7] | |
FromTuple (a1, a2, a3, a4, a5, a6, a7, a8) = '[a1, a2, a3, a4, a5, a6, a7, a8] | |
FromTuple (a1, a2, a3, a4, a5, a6, a7, a8, a9) = '[a1, a2, a3, a4, a5, a6, a7, a8, a9] | |
FromTuple (a1, a2, a3, a4, a5, a6, a7, a8, a9, a10) = '[a1, a2, a3, a4, a5, a6, a7, a8, a9, a10] |
type family Length (xs :: [k]) :: Nat where ... Source #
A type family that returns the length of a type-level list.
type family All (c :: Type -> Constraint) (ts :: [Type]) = (p :: Constraint) | p -> ts where ... Source #
Apply constraint to all types in a list.
Internal
compileToCallable :: forall n a f. (FuncBuilder f n a, IsHalideKernel (LoweredSignature f)) => Target -> f -> IO (Callable (LoweredSignature f)) Source #
A test that tries to compile and run a Halide pipeline using FeatureCUDA
.
This is implemented fully in C++ to make sure that we test the installation rather than our Haskell code.
On non-NixOS systems one should do the following:
nixGLNvidia cabal repl --ghc-options='-fobject-code -O0' ghci> testCUDA
testOpenCL :: IO () Source #
Similar to testCUDA
but for FeatureOpenCL
.
data SomeLoopLevel where Source #
SomeLoopLevel :: LoopLevel t -> SomeLoopLevel |
Instances
Show SomeLoopLevel Source # | |
Defined in Language.Halide.LoopLevel | |
Eq SomeLoopLevel Source # | |
Defined in Language.Halide.LoopLevel (==) :: SomeLoopLevel -> SomeLoopLevel -> Bool Source # (/=) :: SomeLoopLevel -> SomeLoopLevel -> Bool Source # |
data RawHalideBuffer Source #
The low-level untyped Haskell analogue of halide_buffer_t
.
It's quite difficult to use RawHalideBuffer
correctly, and misusage can result in crashes and
segmentation faults. Hence, prefer the higher-level HalideBuffer
wrapper for all your code
RawHalideBuffer | |
|
Instances
Storable RawHalideBuffer Source # | |
Defined in Language.Halide.Buffer sizeOf :: RawHalideBuffer -> Int Source # alignment :: RawHalideBuffer -> Int Source # peekElemOff :: Ptr RawHalideBuffer -> Int -> IO RawHalideBuffer Source # pokeElemOff :: Ptr RawHalideBuffer -> Int -> RawHalideBuffer -> IO () Source # peekByteOff :: Ptr b -> Int -> IO RawHalideBuffer Source # pokeByteOff :: Ptr b -> Int -> RawHalideBuffer -> IO () Source # peek :: Ptr RawHalideBuffer -> IO RawHalideBuffer Source # poke :: Ptr RawHalideBuffer -> RawHalideBuffer -> IO () Source # | |
Show RawHalideBuffer Source # | |
Defined in Language.Halide.Buffer | |
Eq RawHalideBuffer Source # | |
Defined in Language.Halide.Buffer (==) :: RawHalideBuffer -> RawHalideBuffer -> Bool Source # (/=) :: RawHalideBuffer -> RawHalideBuffer -> Bool Source # |
data HalideDimension Source #
Information about a dimension in a buffer.
It is the Haskell analogue of halide_dimension_t
.
HalideDimension | |
|
Instances
Storable HalideDimension Source # | |
Defined in Language.Halide.Buffer sizeOf :: HalideDimension -> Int Source # alignment :: HalideDimension -> Int Source # peekElemOff :: Ptr HalideDimension -> Int -> IO HalideDimension Source # pokeElemOff :: Ptr HalideDimension -> Int -> HalideDimension -> IO () Source # peekByteOff :: Ptr b -> Int -> IO HalideDimension Source # pokeByteOff :: Ptr b -> Int -> HalideDimension -> IO () Source # peek :: Ptr HalideDimension -> IO HalideDimension Source # poke :: Ptr HalideDimension -> HalideDimension -> IO () Source # | |
Read HalideDimension Source # | |
Defined in Language.Halide.Buffer | |
Show HalideDimension Source # | |
Defined in Language.Halide.Buffer | |
Eq HalideDimension Source # | |
Defined in Language.Halide.Buffer (==) :: HalideDimension -> HalideDimension -> Bool Source # (/=) :: HalideDimension -> HalideDimension -> Bool Source # |
data HalideDeviceInterface Source #
Haskell analogue of halide_device_interface_t
.
:: Integral a | |
=> [a] | Extents |
-> [a] |
Get strides corresponding to row-major ordering
:: Integral a | |
=> [a] | Extents |
-> [a] |
Get strides corresponding to column-major ordering.
isDeviceDirty :: Ptr RawHalideBuffer -> IO Bool Source #
Do we have changes on the device the have not been copied to the host?
isHostDirty :: Ptr RawHalideBuffer -> IO Bool Source #
Do we have changes on the device the have not been copied to the host?
getBufferExtent :: forall n a. KnownNat n => Ptr (HalideBuffer n a) -> Int -> IO Int Source #
bufferCopyToHost :: HasCallStack => Ptr RawHalideBuffer -> IO () Source #
Copy the underlying memory from device to host.
withCopiedToHost :: Ptr (HalideBuffer n a) -> IO b -> IO b Source #
withCopiedToHost buf action
performs the action action
ensuring that buf
has been
copied to the host beforehand. If buf
is already on the host, no copying is performed.
:: Ptr (HalideBuffer n a) | buffer |
-> Int | dimension |
-> Int | min |
-> Int | extent |
-> (Ptr (HalideBuffer n a) -> IO b) | what to do |
-> IO b |
Perform an action on a cropped buffer.
Instances
Storable Dim Source # | |
Defined in Language.Halide.Schedule | |
Show Dim Source # | |
Eq Dim Source # | |
Type of dimension that tells which transformations are legal on it.
Instances
Enum DimType Source # | |
Defined in Language.Halide.Schedule succ :: DimType -> DimType Source # pred :: DimType -> DimType Source # toEnum :: Int -> DimType Source # fromEnum :: DimType -> Int Source # enumFrom :: DimType -> [DimType] Source # enumFromThen :: DimType -> DimType -> [DimType] Source # enumFromTo :: DimType -> DimType -> [DimType] Source # enumFromThenTo :: DimType -> DimType -> DimType -> [DimType] Source # | |
Show DimType Source # | |
Eq DimType Source # | |
Specifies how loop values are traversed.
Instances
Enum ForType Source # | |
Defined in Language.Halide.Schedule succ :: ForType -> ForType Source # pred :: ForType -> ForType Source # toEnum :: Int -> ForType Source # fromEnum :: ForType -> Int Source # enumFrom :: ForType -> [ForType] Source # enumFromThen :: ForType -> ForType -> [ForType] Source # enumFromTo :: ForType -> ForType -> [ForType] Source # enumFromThenTo :: ForType -> ForType -> ForType -> [ForType] Source # | |
Show ForType Source # | |
Eq ForType Source # | |
data SplitContents Source #
SplitContents | |
|
Instances
Show SplitContents Source # | |
Defined in Language.Halide.Schedule |
data FuseContents Source #
Instances
Show FuseContents Source # | |
Defined in Language.Halide.Schedule | |
Eq FuseContents Source # | |
Defined in Language.Halide.Schedule (==) :: FuseContents -> FuseContents -> Bool Source # (/=) :: FuseContents -> FuseContents -> Bool Source # |
Instances
Storable Split Source # | |
Defined in Language.Halide.Schedule sizeOf :: Split -> Int Source # alignment :: Split -> Int Source # peekElemOff :: Ptr Split -> Int -> IO Split Source # pokeElemOff :: Ptr Split -> Int -> Split -> IO () Source # peekByteOff :: Ptr b -> Int -> IO Split Source # pokeByteOff :: Ptr b -> Int -> Split -> IO () Source # | |
Show Split Source # | |
data StorageDim Source #
StorageDim | |
|
Instances
Show StorageDim Source # | |
Defined in Language.Halide.Schedule |
Instances
Storable FusedPair Source # | |
Defined in Language.Halide.Schedule sizeOf :: FusedPair -> Int Source # alignment :: FusedPair -> Int Source # peekElemOff :: Ptr FusedPair -> Int -> IO FusedPair Source # pokeElemOff :: Ptr FusedPair -> Int -> FusedPair -> IO () Source # peekByteOff :: Ptr b -> Int -> IO FusedPair Source # pokeByteOff :: Ptr b -> Int -> FusedPair -> IO () Source # | |
Show FusedPair Source # | |
Eq FusedPair Source # | |
data FuseLoopLevel Source #
Instances
Show FuseLoopLevel Source # | |
Defined in Language.Halide.Schedule | |
Eq FuseLoopLevel Source # | |
Defined in Language.Halide.Schedule (==) :: FuseLoopLevel -> FuseLoopLevel -> Bool Source # (/=) :: FuseLoopLevel -> FuseLoopLevel -> Bool Source # |
data StageSchedule Source #
StageSchedule | |
|
Instances
Show StageSchedule Source # | |
Defined in Language.Halide.Schedule |
data ReductionVariable Source #
Instances
Storable ReductionVariable Source # | |
Defined in Language.Halide.Schedule sizeOf :: ReductionVariable -> Int Source # alignment :: ReductionVariable -> Int Source # peekElemOff :: Ptr ReductionVariable -> Int -> IO ReductionVariable Source # pokeElemOff :: Ptr ReductionVariable -> Int -> ReductionVariable -> IO () Source # peekByteOff :: Ptr b -> Int -> IO ReductionVariable Source # pokeByteOff :: Ptr b -> Int -> ReductionVariable -> IO () Source # peek :: Ptr ReductionVariable -> IO ReductionVariable Source # poke :: Ptr ReductionVariable -> ReductionVariable -> IO () Source # | |
Show ReductionVariable Source # | |
Defined in Language.Halide.Schedule |
data PrefetchDirective Source #
PrefetchDirective | |
|
Instances
Storable PrefetchDirective Source # | |
Defined in Language.Halide.Schedule sizeOf :: PrefetchDirective -> Int Source # alignment :: PrefetchDirective -> Int Source # peekElemOff :: Ptr PrefetchDirective -> Int -> IO PrefetchDirective Source # pokeElemOff :: Ptr PrefetchDirective -> Int -> PrefetchDirective -> IO () Source # peekByteOff :: Ptr b -> Int -> IO PrefetchDirective Source # pokeByteOff :: Ptr b -> Int -> PrefetchDirective -> IO () Source # peek :: Ptr PrefetchDirective -> IO PrefetchDirective Source # poke :: Ptr PrefetchDirective -> PrefetchDirective -> IO () Source # | |
Show PrefetchDirective Source # | |
Defined in Language.Halide.Schedule |
getStageSchedule :: Stage n a -> IO StageSchedule Source #
data AutoScheduler Source #
Instances
Show AutoScheduler Source # | |
Defined in Language.Halide.Schedule | |
Eq AutoScheduler Source # | |
Defined in Language.Halide.Schedule (==) :: AutoScheduler -> AutoScheduler -> Bool Source # (/=) :: AutoScheduler -> AutoScheduler -> Bool Source # |
loadAutoScheduler :: AutoScheduler -> IO () Source #
applyAutoScheduler :: KnownNat n => AutoScheduler -> Target -> Func t n a -> IO Text Source #
applySchedule :: KnownNat n => StageSchedule -> Stage n a -> IO () Source #
type family FunctionArguments (f :: Type) :: [Type] where ... Source #
Return the list of arguments to of a function type.
FunctionArguments (a -> b) = a ': FunctionArguments b | |
FunctionArguments a = '[] |
type family FunctionReturn (f :: Type) :: Type where ... Source #
Get the return type of a function.
FunctionReturn (a -> b) = FunctionReturn b | |
FunctionReturn a = a |
class Curry (args :: [Type]) (r :: Type) (f :: Type) | args r -> f where Source #
A helper typeclass to convert a function that takes Arguments
as input
into a normal curried function. This is the inverse of UnCurry
.
For instance, if we have a function f :: Arguments '[Int, Float] -> Double
, then
it will be converted to f' :: Int -> Float -> Double
.
class UnCurry (f :: Type) (args :: [Type]) (r :: Type) | args r -> f, args f -> r where Source #
A helper typeclass to convert a normal curried function to a function that
takes Arguments
as input.
For instance, if we have a function f :: Int -> Float -> Double
, then it
will be converted to f' :: Arguments '[Int, Float] -> Double
.
Instances
(FunctionArguments f ~ ('[] :: [Type]), FunctionReturn f ~ r, f ~ r) => UnCurry f ('[] :: [Type]) r Source # | |
Defined in Language.Halide.Type | |
UnCurry f args r => UnCurry (a -> f) (a ': args) r Source # | |
Defined in Language.Halide.Type |
type family LoweredSignature f where ... Source #
LoweredSignature (Expr a -> r) = a -> LoweredSignature r | |
LoweredSignature (Func t n (Expr a) -> r) = Ptr (HalideBuffer n a) -> LoweredSignature r | |
LoweredSignature (IO (Func t n (Expr a))) = Ptr (HalideBuffer n a) -> IO () | |
LoweredSignature (IO (Func t n (Expr a1, Expr a2))) = Ptr (HalideBuffer n a1) -> Ptr (HalideBuffer n a2) -> IO () |
inline-c helpers
importHalide :: DecsQ Source #
One stop function to include all the neccessary machinery to call Halide functions via inline-c.
Put importHalide
somewhere at the beginning of the file and enjoy using the C++ interface of
Halide via inline-c quasiquotes.
testWriteToStderr :: IO () Source #
data CxxParameter Source #
Haskell counterpart of Halide::Internal::Parameter
.
data CxxImageParam Source #
Haskell counterpart of Halide::ImageParam
.
Haskell counterpart of Halide::Stage.
data CxxDimension Source #
Haskell counterpart of Halide::Internal::Dimension
.
data CxxLoopLevel Source #
Haskell counterpart of Halide::LoopLevel
Convenience re-exports
32-bit signed integer type
Instances
A value of type
represents a pointer to an object, or an
array of objects, which may be marshalled to or from Haskell values
of type Ptr
aa
.
The type a
will often be an instance of class
Storable
which provides the marshalling operations.
However this is not essential, and you can provide your own operations
to access the pointer. For example you might write small foreign
functions to get or set the fields of a C struct
.
Instances
Generic1 (URec (Ptr ()) :: k -> Type) | |
Foldable (UAddr :: TYPE LiftedRep -> Type) | Since: base-4.9.0.0 |
Defined in Data.Foldable fold :: Monoid m => UAddr m -> m Source # foldMap :: Monoid m => (a -> m) -> UAddr a -> m Source # foldMap' :: Monoid m => (a -> m) -> UAddr a -> m Source # foldr :: (a -> b -> b) -> b -> UAddr a -> b Source # foldr' :: (a -> b -> b) -> b -> UAddr a -> b Source # foldl :: (b -> a -> b) -> b -> UAddr a -> b Source # foldl' :: (b -> a -> b) -> b -> UAddr a -> b Source # foldr1 :: (a -> a -> a) -> UAddr a -> a Source # foldl1 :: (a -> a -> a) -> UAddr a -> a Source # toList :: UAddr a -> [a] Source # null :: UAddr a -> Bool Source # length :: UAddr a -> Int Source # elem :: Eq a => a -> UAddr a -> Bool Source # maximum :: Ord a => UAddr a -> a Source # minimum :: Ord a => UAddr a -> a Source # | |
Traversable (UAddr :: Type -> Type) | Since: base-4.9.0.0 |
Storable (Ptr a) | Since: base-2.1 |
Defined in Foreign.Storable sizeOf :: Ptr a -> Int Source # alignment :: Ptr a -> Int Source # peekElemOff :: Ptr (Ptr a) -> Int -> IO (Ptr a) Source # pokeElemOff :: Ptr (Ptr a) -> Int -> Ptr a -> IO () Source # peekByteOff :: Ptr b -> Int -> IO (Ptr a) Source # pokeByteOff :: Ptr b -> Int -> Ptr a -> IO () Source # | |
Show (Ptr a) | Since: base-2.1 |
Eq (Ptr a) | Since: base-2.1 |
Ord (Ptr a) | Since: base-2.1 |
Hashable (Ptr a) | |
Prim (Ptr a) | |
Defined in Data.Primitive.Types sizeOf# :: Ptr a -> Int# Source # alignment# :: Ptr a -> Int# Source # indexByteArray# :: ByteArray# -> Int# -> Ptr a Source # readByteArray# :: MutableByteArray# s -> Int# -> State# s -> (# State# s, Ptr a #) Source # writeByteArray# :: MutableByteArray# s -> Int# -> Ptr a -> State# s -> State# s Source # setByteArray# :: MutableByteArray# s -> Int# -> Int# -> Ptr a -> State# s -> State# s Source # indexOffAddr# :: Addr# -> Int# -> Ptr a Source # readOffAddr# :: Addr# -> Int# -> State# s -> (# State# s, Ptr a #) Source # writeOffAddr# :: Addr# -> Int# -> Ptr a -> State# s -> State# s Source # setOffAddr# :: Addr# -> Int# -> Int# -> Ptr a -> State# s -> State# s Source # | |
Functor (URec (Ptr ()) :: TYPE LiftedRep -> Type) | Since: base-4.9.0.0 |
Generic (URec (Ptr ()) p) | |
Eq (URec (Ptr ()) p) | Since: base-4.9.0.0 |
Ord (URec (Ptr ()) p) | Since: base-4.9.0.0 |
Defined in GHC.Generics compare :: URec (Ptr ()) p -> URec (Ptr ()) p -> Ordering Source # (<) :: URec (Ptr ()) p -> URec (Ptr ()) p -> Bool Source # (<=) :: URec (Ptr ()) p -> URec (Ptr ()) p -> Bool Source # (>) :: URec (Ptr ()) p -> URec (Ptr ()) p -> Bool Source # (>=) :: URec (Ptr ()) p -> URec (Ptr ()) p -> Bool Source # max :: URec (Ptr ()) p -> URec (Ptr ()) p -> URec (Ptr ()) p Source # min :: URec (Ptr ()) p -> URec (Ptr ()) p -> URec (Ptr ()) p Source # | |
data URec (Ptr ()) (p :: k) | Used for marking occurrences of Since: base-4.9.0.0 |
type Rep1 (URec (Ptr ()) :: k -> Type) | Since: base-4.9.0.0 |
Defined in GHC.Generics | |
type Rep (URec (Ptr ()) p) | Since: base-4.9.0.0 |
Defined in GHC.Generics |