content: -- | Some GPU platforms have a SIMD/warp/wavefront-based execution
  -- model that execute blocks of threads in lockstep, permitting us to
  -- perform cross-thread synchronisation within each such block without
  -- the use of barriers. Unfortunately, there seems to be no reliable
  -- way to query these sizes at runtime. Instead, we use builtin tables
  -- to figure out which size we should use for a specific platform and
  -- device. If nothing matches here, the wave size should be set to
  -- one.
  --
  -- We also use this to select reasonable default block sizes and block
  -- counts.
  module Futhark.CodeGen.OpenCL.Heuristics
    ( SizeHeuristic (..),
      DeviceType (..),
      WhichSize (..),
      DeviceInfo (..),
      sizeHeuristicsTable,
    )
  where
  
  import Futhark.Analysis.PrimExp
  import Futhark.Util.Pretty
  
  -- | The type of OpenCL device that this heuristic applies to.
  data DeviceType = DeviceCPU | DeviceGPU
  
  -- | The value supplies by a heuristic can depend on some device
  -- information.  This will be translated into a call to
  -- @clGetDeviceInfo()@. Make sure to only request info that can be
  -- casted to a scalar type.
  newtype DeviceInfo = DeviceInfo String
  
  instance Pretty DeviceInfo where
    pretty (DeviceInfo s) = "device_info" <> parens (pretty s)
  
  -- | A size that can be assigned a default.
  data WhichSize = LockstepWidth | NumBlocks | BlockSize | TileSize | RegTileSize | Threshold
  
  -- | A heuristic for setting the default value for something.
  data SizeHeuristic = SizeHeuristic
    { platformName :: String,
      deviceType :: DeviceType,
      heuristicSize :: WhichSize,
      heuristicValue :: TPrimExp Int32 DeviceInfo
    }
  
  -- | All of our heuristics.
  sizeHeuristicsTable :: [SizeHeuristic]
  sizeHeuristicsTable =
    [ SizeHeuristic "NVIDIA CUDA" DeviceGPU LockstepWidth 32,
      SizeHeuristic "AMD Accelerated Parallel Processing" DeviceGPU LockstepWidth 32,
      SizeHeuristic "rusticl" DeviceGPU LockstepWidth 32,
      SizeHeuristic "" DeviceGPU LockstepWidth 1,
      -- We calculate the number of blocks to aim for 1024 threads per
      -- compute unit if we also use the default block size.  This seems
      -- to perform well in practice.
      SizeHeuristic "" DeviceGPU NumBlocks $ 4 * max_compute_units,
      SizeHeuristic "" DeviceGPU BlockSize 256,
      SizeHeuristic "" DeviceGPU TileSize 32,
      SizeHeuristic "" DeviceGPU RegTileSize 2,
      SizeHeuristic "" DeviceGPU Threshold $ 32 * 1024,
      --
      SizeHeuristic "" DeviceCPU LockstepWidth 1,
      SizeHeuristic "" DeviceCPU NumBlocks max_compute_units,
      SizeHeuristic "" DeviceCPU BlockSize 32,
      SizeHeuristic "" DeviceCPU TileSize 4,
      SizeHeuristic "" DeviceCPU RegTileSize 1,
      SizeHeuristic "" DeviceCPU Threshold max_compute_units
    ]
    where
      max_compute_units =
        TPrimExp $ LeafExp (DeviceInfo "MAX_COMPUTE_UNITS") $ IntType Int32
  