    {-# LANGUAGE CPP, GADTs #-}
    {-# OPTIONS_GHC -Wall -fno-warn-name-shadowing #-}
    #if __GLASGOW_HASKELL__ >= 701
    {-# LANGUAGE Safe #-}
    #endif

    module Compiler.Hoopl.Passes.DList
      ( Doms, domEntry, domLattice
      , domPass
      )
    where

    import Compiler.Hoopl

    type Doms = WithBot [Label]
    -- ^ List of labels, extended with a standard bottom element

    -- | The fact that goes into the entry of a dominator analysis: the first node
    -- is dominated only by the entry point, which is represented by the empty list
    -- of labels.
    domEntry :: Doms
    domEntry = PElem []

    domLattice :: DataflowLattice Doms
    domLattice = addPoints "dominators" extend

    extend :: JoinFun [Label]
    extend _ (OldFact l) (NewFact l') = (changeIf (l `lengthDiffers` j), j)
        where j = lcs l l'
              lcs :: [Label] -> [Label] -> [Label] -- longest common suffix
              lcs l l' | length l > length l' = lcs (drop (length l - length l') l) l'
                       | length l < length l' = lcs l' l
                       | otherwise = dropUnlike l l' l
              dropUnlike [] [] maybe_like = maybe_like
              dropUnlike (x:xs) (y:ys) maybe_like =
                  dropUnlike xs ys (if x == y then maybe_like else xs)
              dropUnlike _ _ _ = error "this can't happen"

              lengthDiffers [] [] = False
              lengthDiffers (_:xs) (_:ys) = lengthDiffers xs ys
              lengthDiffers [] (_:_) = True
              lengthDiffers (_:_) [] = True

    -- | Dominator pass
    domPass :: (NonLocal n, Monad m) => FwdPass m n Doms
    domPass = FwdPass domLattice (mkFTransfer3 first (const id) distributeFact) noFwdRewrite
      where first n = fmap (entryLabel n:)