packages feed

futhark-0.15.7: src/Futhark/Pass/ExplicitAllocations/Kernels.hs

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE FlexibleInstances #-}
{-# LANGUAGE TypeFamilies #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Futhark.Pass.ExplicitAllocations.Kernels
       ( explicitAllocations
       , explicitAllocationsInStms
       )
where

import qualified Data.Map as M
import qualified Data.Set as S

import qualified Futhark.Representation.Mem.IxFun as IxFun
import Futhark.Representation.KernelsMem
import Futhark.Representation.Kernels
import Futhark.Pass.ExplicitAllocations
import Futhark.Pass.ExplicitAllocations.SegOp

instance SizeSubst (HostOp lore op) where
  opSizeSubst (Pattern _ [size]) (SizeOp (SplitSpace _ _ _ elems_per_thread)) =
    M.singleton (patElemName size) elems_per_thread
  opSizeSubst _ _ = mempty

  opIsConst (SizeOp GetSize{}) = True
  opIsConst (SizeOp GetSizeMax{}) = True
  opIsConst _ = False

instance SizeSubst (SegOp lvl lore) where
  opSizeSubst _ _ = mempty

allocAtLevel :: SegLevel -> AllocM fromlore tlore a -> AllocM fromlore tlore a
allocAtLevel lvl = local $ \env -> env { allocSpace = space
                                       , aggressiveReuse = True
                                       }
  where space = case lvl of SegThread{} -> DefaultSpace
                            SegGroup{} -> Space "local"

handleSegOp :: SegOp SegLevel Kernels
            -> AllocM Kernels KernelsMem (SegOp SegLevel KernelsMem)
handleSegOp op = do
  num_threads <- letSubExp "num_threads" $ BasicOp $ BinOp (Mul Int32 OverflowUndef)
                 (unCount (segNumGroups lvl)) (unCount (segGroupSize lvl))
  allocAtLevel lvl $ mapSegOpM (mapper num_threads) op
  where scope = scopeOfSegSpace $ segSpace op
        lvl = segLevel op
        mapper num_threads =
          identitySegOpMapper
          { mapOnSegOpBody =
              localScope scope . local f . allocInKernelBody
          , mapOnSegOpLambda =
              local inThread .
              allocInBinOpLambda num_threads (segSpace op)
          }
        f = case segLevel op of SegThread{} -> inThread
                                SegGroup{} -> inGroup
        inThread env = env { envExpHints = inThreadExpHints }
        inGroup env = env { envExpHints = inGroupExpHints }

handleHostOp :: HostOp Kernels (SOAC Kernels)
             -> AllocM Kernels KernelsMem (MemOp (HostOp KernelsMem ()))
handleHostOp (SizeOp op) =
  return $ Inner $ SizeOp op
handleHostOp (OtherOp op) =
  error $ "Cannot allocate memory in SOAC: " ++ pretty op
handleHostOp (SegOp op) =
  Inner . SegOp <$> handleSegOp op

kernelExpHints :: Allocator KernelsMem m => Exp KernelsMem -> m [ExpHint]
kernelExpHints (BasicOp (Manifest perm v)) = do
  dims <- arrayDims <$> lookupType v
  let perm_inv = rearrangeInverse perm
      dims' = rearrangeShape perm dims
      ixfun = IxFun.permute (IxFun.iota $ map (primExpFromSubExp int32) dims')
              perm_inv
  return [Hint ixfun DefaultSpace]

kernelExpHints (Op (Inner (SegOp (SegMap lvl@SegThread{} space ts body)))) =
  zipWithM (mapResultHint lvl space) ts $ kernelBodyResult body

kernelExpHints (Op (Inner (SegOp (SegRed lvl@SegThread{} space reds ts body)))) =
  (map (const NoHint) red_res <>) <$> zipWithM (mapResultHint lvl space) (drop num_reds ts) map_res
  where num_reds = segBinOpResults reds
        (red_res, map_res) = splitAt num_reds $ kernelBodyResult body

kernelExpHints e =
  return $ replicate (expExtTypeSize e) NoHint

mapResultHint :: Allocator lore m =>
                 SegLevel -> SegSpace -> Type -> KernelResult -> m ExpHint
mapResultHint lvl space = hint
  where num_threads = primExpFromSubExp int32 (unCount $ segNumGroups lvl) *
                      primExpFromSubExp int32 (unCount $ segGroupSize lvl)

        -- Heuristic: do not rearrange for returned arrays that are
        -- sufficiently small.
        coalesceReturnOfShape _ [] = False
        coalesceReturnOfShape bs [Constant (IntValue (Int32Value d))] = bs * d > 4
        coalesceReturnOfShape _ _ = True

        hint t Returns{}
          | coalesceReturnOfShape (primByteSize (elemType t)) $ arrayDims t = do
              let space_dims = segSpaceDims space
              t_dims <- mapM dimAllocationSize $ arrayDims t
              return $ Hint (innermost space_dims t_dims) DefaultSpace

        hint t (ConcatReturns SplitStrided{} w _ _) = do
          t_dims <- mapM dimAllocationSize $ arrayDims t
          return $ Hint (innermost [w] t_dims) DefaultSpace

        hint Prim{} (ConcatReturns SplitContiguous w elems_per_thread _) = do
          let ixfun_base = IxFun.iota [num_threads, primExpFromSubExp int32 elems_per_thread]
              ixfun_tr = IxFun.permute ixfun_base [1,0]
              ixfun = IxFun.reshape ixfun_tr $ map (DimNew . primExpFromSubExp int32) [w]
          return $ Hint ixfun DefaultSpace

        hint _ _ = return NoHint

innermost :: [SubExp] -> [SubExp] -> IxFun
innermost space_dims t_dims =
  let r = length t_dims
      dims = space_dims ++ t_dims
      perm = [length space_dims..length space_dims+r-1] ++
             [0..length space_dims-1]
      perm_inv = rearrangeInverse perm
      dims_perm = rearrangeShape perm dims
      ixfun_base = IxFun.iota $ map (primExpFromSubExp int32) dims_perm
      ixfun_rearranged = IxFun.permute ixfun_base perm_inv
  in ixfun_rearranged

inGroupExpHints :: Exp KernelsMem -> AllocM Kernels KernelsMem [ExpHint]
inGroupExpHints (Op (Inner (SegOp (SegMap _ space ts body))))
  | any private $ kernelBodyResult body = return $ do
      (t, r) <- zip ts $ kernelBodyResult body
      return $
        if private r
        then let seg_dims = map (primExpFromSubExp int32) $ segSpaceDims space
                 dims = seg_dims ++ map (primExpFromSubExp int32) (arrayDims t)
                 nilSlice d = DimSlice 0 d 0
           in Hint (IxFun.slice (IxFun.iota dims) $
                    fullSliceNum dims $ map nilSlice seg_dims) $
              ScalarSpace (arrayDims t) $ elemType t
        else NoHint
  where private (Returns ResultPrivate _) = True
        private _                         = False
inGroupExpHints e = return $ replicate (expExtTypeSize e) NoHint

inThreadExpHints :: Allocator KernelsMem m => Exp KernelsMem -> m [ExpHint]
inThreadExpHints e = do
  consts <- askConsts
  mapM (maybePrivate consts) =<< expExtType e
  where maybePrivate consts t
          | Just (Array pt shape _) <- hasStaticShape t,
            all (semiStatic consts) $ shapeDims shape = do
              let ixfun = IxFun.iota $ map (primExpFromSubExp int32) $
                          shapeDims shape
              return $ Hint ixfun $ ScalarSpace (shapeDims shape) pt
          | otherwise =
              return NoHint

        semiStatic _ Constant{} = True
        semiStatic consts (Var v) = v `S.member` consts

explicitAllocations :: Pass Kernels KernelsMem
explicitAllocations = explicitAllocationsGeneric handleHostOp kernelExpHints

explicitAllocationsInStms :: (MonadFreshNames m, HasScope KernelsMem m) =>
                             Stms Kernels -> m (Stms KernelsMem)
explicitAllocationsInStms = explicitAllocationsInStmsGeneric handleHostOp kernelExpHints