expandedAllocations :: (SubExp,SubExp, SubExp)
                    -> (VName, VName, VName)
                    -> M.Map VName (SubExp, Space)
                    -> ExpandM ([Stm ExplicitMemory], RebaseMap)
expandedAllocations (num_threads64, num_groups, group_size) (_thread_index, group_id, local_id) thread_allocs = do
  -- We expand the allocations by multiplying their size with the
  -- number of kernel threads.
  (alloc_bnds, rebase_map) <- unzip <$> mapM expand (M.toList thread_allocs)

  -- Fix every reference to the memory blocks to be offset by the
  -- thread number.
  let alloc_offsets =
        RebaseMap { rebaseMap = mconcat rebase_map
                  , indexVariable = (group_id, local_id)
                  , kernelWidth = (num_groups, group_size)
                  }
  return (concat alloc_bnds, alloc_offsets)
  where expand (mem, (per_thread_size, Space "local")) = do
          let allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem per_thread_size $ Space "local"]
          return ([Let allocpat (defAux ()) $ Op $ Alloc per_thread_size $ Space "local"],
                  mempty)

        expand (mem, (per_thread_size, space)) = do
          total_size <- newVName "total_size"
          let sizepat = Pattern [] [PatElem total_size BindVar $ MemPrim int64]
              allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem (Var total_size) space]
          return ([Let sizepat (defAux ()) $
                    BasicOp $ BinOp (Mul Int64) num_threads64 per_thread_size,
                   Let allocpat (defAux ()) $
                    Op $ Alloc (Var total_size) space],
                   M.singleton mem newBase)

        newBase old_shape =
          let num_dims = length old_shape
              perm = [0, num_dims+1] ++ [1..num_dims]
              root_ixfun = IxFun.iota (primExpFromSubExp int32 num_groups : old_shape
                                       ++ [primExpFromSubExp int32 group_size])
              permuted_ixfun = IxFun.permute root_ixfun perm
              untouched d = DimSlice 0 d 1
              offset_ixfun = IxFun.slice permuted_ixfun $
                             [DimFix (LeafExp group_id int32),
                              DimFix (LeafExp local_id int32)] ++
                             map untouched old_shape
          in offset_ixfun
expandedAllocations :: (SubExp,SubExp, SubExp)
                    -> (VName, VName, VName)
                    -> M.Map VName (SubExp, Space)
                    -> ExpandM ([Stm ExplicitMemory], RebaseMap)
expandedAllocations (num_threads64, num_groups, group_size) (_thread_index, group_id, local_id) thread_allocs = do
  -- We expand the allocations by multiplying their size with the
  -- number of kernel threads.
  (alloc_bnds, rebase_map) <- unzip <$> mapM expand (M.toList thread_allocs)

  -- Fix every reference to the memory blocks to be offset by the
  -- thread number.
  let alloc_offsets =
        RebaseMap { rebaseMap = mconcat rebase_map
                  , indexVariable = (group_id, local_id)
                  , kernelWidth = (num_groups, group_size)
                  }
  return (concat alloc_bnds, alloc_offsets)
  where expand (mem, (per_thread_size, Space "local")) = do
          let allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem per_thread_size $ Space "local"]
          return ([Let allocpat (defAux ()) $ Op $ Alloc per_thread_size $ Space "local"],
                  mempty)

        expand (mem, (per_thread_size, space)) = do
          total_size <- newVName "total_size"
          let sizepat = Pattern [] [PatElem total_size BindVar $ MemPrim int64]
              allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem (Var total_size) space]
          return ([Let sizepat (defAux ()) $
                    BasicOp $ BinOp (Mul Int64) num_threads64 per_thread_size,
                   Let allocpat (defAux ()) $
                    Op $ Alloc (Var total_size) space],
                   M.singleton mem newBase)

        newBase old_shape =
          let num_dims = length old_shape
              perm = [0, num_dims+1] ++ [1..num_dims]
              root_ixfun = IxFun.iota (primExpFromSubExp int32 num_groups : old_shape
                                       ++ [primExpFromSubExp int32 group_size])
              permuted_ixfun = IxFun.permute root_ixfun perm
              untouched d = DimSlice 0 d 1
              offset_ixfun = IxFun.slice permuted_ixfun $
                             [DimFix (LeafExp group_id int32),
                              DimFix (LeafExp local_id int32)] ++
                             map untouched old_shape
          in offset_ixfun
expandedAllocations (num_threads64, num_groups, group_size) (_thread_index, group_id, local_id) thread_allocs = do
  -- We expand the allocations by multiplying their size with the
  -- number of kernel threads.
  (alloc_bnds, rebase_map) <- unzip <$> mapM expand (M.toList thread_allocs)

  -- Fix every reference to the memory blocks to be offset by the
  -- thread number.
  let alloc_offsets =
        RebaseMap { rebaseMap = mconcat rebase_map
                  , indexVariable = (group_id, local_id)
                  , kernelWidth = (num_groups, group_size)
                  }
  return (concat alloc_bnds, alloc_offsets)
  where expand (mem, (per_thread_size, Space "local")) = do
          let allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem per_thread_size $ Space "local"]
          return ([Let allocpat (defAux ()) $ Op $ Alloc per_thread_size $ Space "local"],
                  mempty)

        expand (mem, (per_thread_size, space)) = do
          total_size <- newVName "total_size"
          let sizepat = Pattern [] [PatElem total_size BindVar $ MemPrim int64]
              allocpat = Pattern [] [PatElem mem BindVar $
                                     MemMem (Var total_size) space]
          return ([Let sizepat (defAux ()) $
                    BasicOp $ BinOp (Mul Int64) num_threads64 per_thread_size,
                   Let allocpat (defAux ()) $
                    Op $ Alloc (Var total_size) space],
                   M.singleton mem newBase)

        newBase old_shape =
          let num_dims = length old_shape
              perm = [0, num_dims+1] ++ [1..num_dims]
              root_ixfun = IxFun.iota (primExpFromSubExp int32 num_groups : old_shape
                                       ++ [primExpFromSubExp int32 group_size])
              permuted_ixfun = IxFun.permute root_ixfun perm
              untouched d = DimSlice 0 d 1
              offset_ixfun = IxFun.slice permuted_ixfun $
                             [DimFix (LeafExp group_id int32),
                              DimFix (LeafExp local_id int32)] ++
                             map untouched old_shape
          in offset_ixfun