futhark-0.7.3: src/Futhark/Optimise/MemoryBlockMerging/Coalescing/AllocationMovingUp.hs
-- | Move allocation statements upwards in the bodies of a program to enable
-- more memory block coalescings.
--
-- This should be run *before* the coalescing pass, as it enables more
-- optimisations.
module Futhark.Optimise.MemoryBlockMerging.Coalescing.AllocationMovingUp
( moveUpAllocsFunDef
) where
import qualified Data.Set as S
import Data.Maybe (mapMaybe)
import Futhark.Representation.AST
import Futhark.Representation.ExplicitMemory (ExplicitMemory)
import qualified Futhark.Representation.ExplicitMemory as ExpMem
import Futhark.Optimise.MemoryBlockMerging.CrudeMovingUp
findAllocHoistees :: Body ExplicitMemory -> Maybe [FParam ExplicitMemory]
-> [VName]
findAllocHoistees body params =
let all_found = mapMaybe findThemStm stms
++ maybe [] (mapMaybe findThemFParam) params
extras = concatMap snd all_found
allocs = map fst all_found
-- We must hoist the alloc expressions in the end. If we hoist an alloc
-- before we hoist one of its array creations (in case of in-place
-- updates), that array creation might in turn hoist something depending
-- on another memory block mem_y further up than the allocation of memory
-- block mem_x. This will become a problem if mem_y can get coalesced
-- into mem_x.
--
-- Maybe there is a nicer way to guarantee that this does not happen, but
-- this seems to work for now.
--
-- We reverse the non-alloc dependencies to ensure (sloppily) that they do
-- not change positions internally compared to the original program: For
-- example, if a statement x is located before a statement y, and both x
-- and y need to be hoisted, then we need to hoist x in the end, so that
-- it can be hoisted further than y, which might have been hoisted to
-- before x. A better solution is welcome!
in reverse extras ++ reverse allocs
where stms :: [Stm ExplicitMemory]
stms = stmsToList $ bodyStms body
findThemStm :: Stm ExplicitMemory -> Maybe (VName, [VName])
findThemStm (Let (Pattern _ [PatElem xmem _]) _ (Op ExpMem.Alloc{})) =
usedByCopyOrConcat xmem
findThemStm _ = Nothing
-- A function paramater can be a unique memory block. While we cannot
-- hoist that, we may have to hoist an index in an in-place update that
-- uses the memory.
findThemFParam :: FParam ExplicitMemory -> Maybe (VName, [VName])
findThemFParam (Param xmem ExpMem.MemMem{}) = usedByCopyOrConcat xmem
findThemFParam _ = Nothing
-- Is the allocated memory used by either Copy or Concat in the function
-- body? Those are the only kinds of memory we care about, since those
-- are the cases handled by coalescing. Also find the names used by
-- in-place updates, since those also need to be hoisted (as an example
-- of this, consider the 'copy/pos1.fut' test where the replicate
-- expression needs to be hoisted as well as its memory allocation).
usedByCopyOrConcat :: VName -> Maybe (VName, [VName])
usedByCopyOrConcat xmem_alloc =
let vs = mapMaybe checkStm stms
vs' = if null vs then Nothing else Just (xmem_alloc, concat vs)
in vs'
where checkStm :: Stm ExplicitMemory -> Maybe [VName]
checkStm (Let
(Pattern _
[PatElem _ (ExpMem.MemArray _ _ _ (ExpMem.ArrayIn xmem_pat _))])
_
(BasicOp bop))
| xmem_pat == xmem_alloc =
case bop of
Update v slice _ ->
-- The source array must also be hoisted so that it
-- is initialized before it is used by the
-- coalesced party. Any index variables are also
-- hoisted.
Just $ v : S.toList (freeIn slice)
Copy{} -> Just []
Concat{} -> Just []
_ -> Nothing
checkStm _ = Nothing
moveUpAllocsFunDef :: FunDef ExplicitMemory
-> FunDef ExplicitMemory
moveUpAllocsFunDef fundef =
moveUpInFunDef fundef findAllocHoistees