packages feed

qute-0.1.0: src/Language/QBE/Analysis/CDG.hs

-- SPDX-FileCopyrightText: 2010 Tristan Ravitch <travitch@cs.wisc.edu>
-- SPDX-FileCopyrightText: 2026 Reliable System Software, Technische Universität Braunschweig <vss@ibr.cs.tu-bs.de>
--
-- SPDX-License-Identifier: BSD-3-Clause AND GPL-3.0-only

-- Based on the implementation provided by LLVM.Analysis.CDG from Tristan Ravitch
-- See https://hackage.haskell.org/package/llvm-analysis-0.3.0/docs/src/LLVM-Analysis-CDG.html
--
-- The implementation by Tristan Ravitch mentions a paper by Cytron et al.
-- See: https://doi.org/10.1145/115372.115320
--
-- However, I found that the original paper by Ferrante et al. does a much better job at
-- explaining what was implemented by Tristan Ravitch in llvm-analysis. Hence, the comments
-- below mainly refer to that: https://doi.org/10.1145/24039.24041

-- | This module implements a control dependency analysis, using a
-- /control dependency graph/ (CDG) for more information on the concept
-- refer to <https://doi.org/10.1145/24039.24041>. Roughly speaking, a
-- node /A/ is control dependent on /B/ if there is an edge /B → A/ so
-- that the node is taken, as well as an edge so that it is not taken.
module Language.QBE.Analysis.CDG
  ( CDG (..),
    build,
    edges,
    ctrlDeps,
  )
where

import Data.Bifunctor (second)
import Data.IntMap (IntMap)
import Data.IntMap qualified as M
import Data.IntSet (IntSet)
import Data.IntSet qualified as S
import Data.List (find)
import Data.Maybe (fromMaybe)
import Language.QBE.Analysis.CFG qualified as CFG
import Language.QBE.Analysis.Graph qualified as G

-- | A CDG signifying control-dependence between nodes in the 'CFG.CFG'.
data CDG
  = CDG
  { -- | Underlying 'CFG.CFG' for which the CDG was built.
    cdgCfg :: CFG.CFG,
    -- | Root node of the t'CDG', used for determining post-dominance.
    cdgRoot :: CFG.Label,
    -- | Graph representation of control-dependence.
    cdgGraph :: G.Graph
  }

-- | All edges of the t'CDG', in an unspecified order.
edges :: CDG -> [(CFG.Label, CFG.Label)]
edges cdg = foldl go [] $ M.toList (cdgGraph cdg)
  where
    go acc (p, c) = acc ++ map (p,) (S.toList c)

-- | Returns the control dependencies of a given node in the 'CFG.CFG'.
-- If the node doesn't have any control dependencies, 'Nothing' is
-- returned.
ctrlDeps :: CDG -> CFG.Label -> Maybe IntSet
ctrlDeps CDG {cdgGraph = cDeps} = (`M.lookup` cDeps)

------------------------------------------------------------------------

-- | Construct a new t'CDG' from an existing 'CFG.CFG'. The CDG is build
-- based on the given 'CFG.Label' from the CFG, which is used to as the
-- root of a post-dominator tree to establish a post-dominance
-- relationship between nodes.
build :: CFG.CFG -> CFG.Label -> CDG
build cfg root =
  CDG
    { cdgCfg = cfg,
      cdgRoot = root,
      cdgGraph = build' cfg root
    }

build' :: CFG.CFG -> CFG.Label -> IntMap IntSet
build' cfg label =
  -- From the CFG, generate a post-dominator tree and also convert this tree
  -- to an IntMap representation for efficient successor lookup in 'addCDGEdge'.
  let rooted = (label, CFG.asDomGraph cfg)
      pdTree = G.pdomTree rooted
      pdtMap = M.fromList $ map (second S.fromList) (G.pdom rooted)
      pdtAnc = M.fromList (G.ancestors pdTree)
   in foldr (uncurry $ addCDGEdge pdtMap pdtAnc) M.empty $ CFG.edges cfg

-- This function essentially implements the algorithm described in Section 3.1
-- of the Paper by Ferrante et al., using the algorithm by Cytron et al. may be
-- more efficient and could be considered in the future.
addCDGEdge ::
  IntMap IntSet ->
  IntMap [Int] ->
  CFG.Label ->
  CFG.Label ->
  IntMap IntSet ->
  IntMap IntSet
addCDGEdge pdtMap pdtAnc a b acc
  -- Consider all edges (A, B) in the control flow graph such that B does not
  -- post-dominate M. If it does, we return 'acc' unmodified (insert nothing).
  | postdominates b a = acc
  | otherwise =
      -- Let AC denote the least common ancestor of A and B in the post-dominator tree.
      case commonAncestor b a of
        -- Case 1: All nodes in the post-dominator tree on the path from AC to
        -- B, including B but not AC, should be made control dependent on A.
        Just ac ->
          let cdepsOnA = S.insert b (S.filter (/= ac) $ lookupSucc b)
           in foldr insertEdge acc (S.toList cdepsOnA)
        -- Case 2: All nodes in the post-dominator tree on the path from A to B,
        -- including A and B, should be made control dependent on A.
        Nothing ->
          let deps = S.insert b $ lookupSucc b
           in foldr insertEdge acc (S.toList deps)
  where
    insertEdge :: CFG.Label -> IntMap IntSet -> IntMap IntSet
    insertEdge blk = M.insertWith S.union blk (S.singleton a)

    lookupSucc :: CFG.Label -> IntSet
    lookupSucc l = fromMaybe S.empty $ M.lookup l pdtMap

    -- Returns true if 'x' post-dominates 'y'.
    postdominates :: CFG.Label -> CFG.Label -> Bool
    postdominates x y = maybe False (x `S.member`) $ M.lookup y pdtMap

    commonAncestor :: G.Node -> G.Node -> Maybe G.Node
    commonAncestor n1 n2 = do
      a1 <- M.lookup n1 pdtAnc
      a2 <- M.lookup n2 pdtAnc
      find (`elem` a1) a2