Skip to content
Snippets Groups Projects
ReduceC.hs 23.7 KiB
Newer Older
  • Learn to ignore specific revisions
  • chrg's avatar
    chrg committed
    {-# LANGUAGE BangPatterns #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE BlockArguments #-}
    
    {-# LANGUAGE ConstraintKinds #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE FlexibleContexts #-}
    
    {-# LANGUAGE FlexibleInstances #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE GADTs #-}
    {-# LANGUAGE LambdaCase #-}
    
    {-# LANGUAGE RankNTypes #-}
    
    {-# LANGUAGE RecordWildCards #-}
    
    {-# LANGUAGE ScopedTypeVariables #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE TupleSections #-}
    
    chrg's avatar
    chrg committed
    {-# LANGUAGE TypeFamilies #-}
    {-# LANGUAGE NoMonomorphismRestriction #-}
    
    
    chrg's avatar
    chrg committed
    module ReduceC (
      defaultReduceC,
    
    chrg's avatar
    chrg committed
      defaultReduceCWithKeywords,
    
    chrg's avatar
    chrg committed
      -- reduceCTranslUnit,
    
    chrg's avatar
    chrg committed
    
      -- * Context
      Context (..),
      defaultContext,
    
      -- * Helpers
      prettyIdent,
    ) where
    
    chrg's avatar
    chrg committed
    
    import Control.Monad.Reduce
    
    import Data.Data
    import Data.Foldable
    
    chrg's avatar
    chrg committed
    import Data.Function
    
    chrg's avatar
    chrg committed
    import Data.Functor
    
    chrg's avatar
    chrg committed
    import qualified Data.List as List
    
    import qualified Data.Map.Strict as Map
    
    chrg's avatar
    chrg committed
    import Data.Maybe
    
    chrg's avatar
    chrg committed
    import qualified Data.Set as Set
    import Data.Vector.Internal.Check (HasCallStack)
    
    chrg's avatar
    chrg committed
    
    -- import Debug.Trace
    
    --
    -- Todo stuckt names
    
    import Control.Applicative
    
    chrg's avatar
    chrg committed
    import Control.Monad
    
    chrg's avatar
    chrg committed
    import qualified Control.Monad.IRTree as IRTree
    
    chrg's avatar
    chrg committed
    import Data.Monoid
    
    chrg's avatar
    chrg committed
    import qualified Language.C as C
    
    chrg's avatar
    chrg committed
    import qualified Language.C.Data.Ident as C
    
    chrg's avatar
    chrg committed
    
    
    data Context = Context
    
    chrg's avatar
    chrg committed
      { keywords :: !(Set.Set Keyword)
    
    chrg's avatar
    chrg committed
      , typeDefs :: !(Map.Map C.Ident InlineType)
      , inlineExprs :: !(Map.Map C.Ident InlineExpr)
    
      , fields :: !(Map.Map C.Ident (Maybe C.Ident))
      , structs :: !(Map.Map C.Ident (Maybe C.CStructUnion))
    
    chrg's avatar
    chrg committed
      deriving (Show)
    
    data InlineType
    
    chrg's avatar
    chrg committed
      = ITKeep
      | ITInline ![C.CDeclarationSpecifier C.NodeInfo]
      deriving (Show, Eq)
    
    data InlineExpr
      = IEDelete
      | IEInline !C.CExpr
      | IEKeep
    
    chrg's avatar
    chrg committed
      deriving (Show, Eq)
    
    chrg's avatar
    chrg committed
    data Keyword
    
    chrg's avatar
    chrg committed
      = LoseMain
    
    chrg's avatar
    chrg committed
      | DoNoops
    
    chrg's avatar
    chrg committed
      | InlineTypeDefs
    
    chrg's avatar
    chrg committed
      | NoSemantics
    
    chrg's avatar
    chrg committed
      | AllowEmptyDeclarations
    
    chrg's avatar
    chrg committed
      | DisallowVariableInlining
    
      | AllowInfiniteForLoops
    
    chrg's avatar
    chrg committed
      deriving (Show, Read, Enum, Eq, Ord)
    
    
    chrg's avatar
    chrg committed
    type Lab = (String, C.Position)
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    defaultReduceCWithKeywords :: (MonadReduce (String, C.Position) m) => [Keyword] -> C.CTranslUnit -> m C.CTranslUnit
    defaultReduceCWithKeywords keywords a = reduceCTranslUnit a (defaultContext{keywords = Set.fromList keywords})
    {-# SPECIALIZE defaultReduceCWithKeywords :: [Keyword] -> C.CTranslUnit -> IRTree.IRTree (String, C.Position) C.CTranslUnit #-}
    
    
    chrg's avatar
    chrg committed
    defaultReduceC :: (MonadReduce (String, C.Position) m) => C.CTranslUnit -> m C.CTranslUnit
    defaultReduceC a = reduceCTranslUnit a defaultContext
    
    chrg's avatar
    chrg committed
    {-# SPECIALIZE defaultReduceC :: C.CTranslUnit -> IRTree.IRTree (String, C.Position) C.CTranslUnit #-}
    
    chrg's avatar
    chrg committed
    addTypeDefs :: [C.Ident] -> InlineType -> Context -> Context
    
    addTypeDefs ids cs Context{..} =
      Context
        { typeDefs =
            foldl' (\a i -> Map.insert i cs a) typeDefs ids
        , ..
        }
    
    
    chrg's avatar
    chrg committed
    addInlineExpr :: C.Ident -> InlineExpr -> Context -> Context
    
    chrg's avatar
    chrg committed
    addInlineExpr i e Context{..} =
      Context
        { inlineExprs = Map.insert i e inlineExprs
        , ..
        }
    
    
    chrg's avatar
    chrg committed
    addKeyword :: Keyword -> Context -> Context
    addKeyword k Context{..} =
      Context
        { keywords = Set.insert k keywords
        , ..
        }
    
    
    addStruct :: StructDef -> Context -> Context
    addStruct (StructDef k fs _) Context{..} =
      Context
        { structs = Map.insert k Nothing structs
        , fields = foldr (`Map.insert` Just k) fields fs
        , ..
        }
    
    removeStruct :: StructDef -> Context -> Context
    removeStruct (StructDef k fs un) Context{..} =
      Context
        { structs = Map.insert k (Just un) structs
        , fields = foldr (`Map.insert` Nothing) fields fs
        , ..
        }
    
    
    chrg's avatar
    chrg committed
    -- deleteKeyword :: Keyword -> Context -> Context
    -- deleteKeyword k Context{..} =
    --   Context
    --     { keywords = Set.delete k keywords
    --     , ..
    --     }
    
    
    defaultContext :: Context
    defaultContext =
      Context
    
    chrg's avatar
    chrg committed
        { keywords = Set.fromList []
    
        , typeDefs = Map.empty
    
    chrg's avatar
    chrg committed
        , inlineExprs =
            Map.fromList
              [ (C.builtinIdent "fabsf", IEKeep)
              , (C.builtinIdent "fabs", IEKeep)
              , (C.builtinIdent "__PRETTY_FUNCTION__", IEKeep)
              , (C.builtinIdent "__FUNCTION__", IEKeep)
              ]
    
        , fields = Map.empty
        , structs = Map.empty
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    isIn :: Keyword -> Context -> Bool
    isIn k = Set.member k . keywords
    
    prettyIdent :: C.Identifier C.NodeInfo -> [Char]
    prettyIdent (C.Ident s _ a) = s ++ " at " ++ show (C.posOfNode a)
    
    reduceCTranslUnit
      :: (MonadReduce Lab m)
      => C.CTranslationUnit C.NodeInfo
      -> Context
      -> m (C.CTranslationUnit C.NodeInfo)
    reduceCTranslUnit (C.CTranslUnit es ni) ctx = do
    
    chrg's avatar
    chrg committed
      res' <- foldr reduceCExternalDeclaration (\_ -> pure []) es ctx
      es' <- sequence res'
    
    chrg's avatar
    chrg committed
      pure $ C.CTranslUnit es' ni
    
    reduceCExternalDeclaration
      :: (MonadReduce Lab m)
      => C.CExternalDeclaration C.NodeInfo
    
    chrg's avatar
    chrg committed
      -> (Context -> m [m (C.CExternalDeclaration C.NodeInfo)])
    
    chrg's avatar
    chrg committed
      -> Context
    
    chrg's avatar
    chrg committed
      -> m [m (C.CExternalDeclaration C.NodeInfo)]
    
    chrg's avatar
    chrg committed
    reduceCExternalDeclaration r cont ctx = do
    
    chrg's avatar
    chrg committed
      -- TODO This is slow
      case r of
    
    chrg's avatar
    chrg committed
        C.CFDefExt fun
    
    chrg's avatar
    chrg committed
          | not (LoseMain `isIn` ctx) && maybe False (("main" ==) . C.identToString) (functionName fun) -> do
    
    chrg's avatar
    chrg committed
              ((C.CFDefExt <$> reduceCFunDef fun ctx) :) <$> cont ctx
    
    chrg's avatar
    chrg committed
          | otherwise ->
    
    chrg's avatar
    chrg committed
              case functionName fun of
                Just fid -> do
                  split
    
    chrg's avatar
    chrg committed
                    ("remove function " <> C.identToString fid, C.posOf r)
    
    chrg's avatar
    chrg committed
                    (cont (addInlineExpr fid IEDelete ctx))
    
    chrg's avatar
    chrg committed
                    do
    
    chrg's avatar
    chrg committed
                      ((C.CFDefExt <$> reduceCFunDef fun ctx) :) <$> cont (addInlineExpr fid IEKeep ctx)
    
    chrg's avatar
    chrg committed
                Nothing -> do
                  split
    
    chrg's avatar
    chrg committed
                    ("remove function", C.posOf r)
    
    chrg's avatar
    chrg committed
                    (cont ctx)
    
    chrg's avatar
    chrg committed
                    (((C.CFDefExt <$> reduceCFunDef fun ctx) :) <$> cont ctx)
    
        C.CDeclExt decl -> do
          (decl', ctx') <- handleDecl decl ctx
    
    chrg's avatar
    chrg committed
          case decl' of
    
    chrg's avatar
    chrg committed
            Just d -> ((C.CDeclExt <$> d) :) <$> cont ctx'
    
    chrg's avatar
    chrg committed
            Nothing -> cont ctx'
    
    chrg's avatar
    chrg committed
        _r -> don'tHandle r
    
    
    data StructDef = StructDef
      { structId :: !C.Ident
      , fieldIds :: ![C.Ident]
      , structDef :: !C.CStructUnion
      }
      deriving (Show, Eq)
    
    structIds
      :: (Foldable f)
      => f (C.CDeclarationSpecifier C.NodeInfo)
      -> [StructDef]
    structIds = concatMap \case
      C.CTypeSpec (C.CSUType (C.CStruct a (Just n) (Just ma) b c) _) ->
        [ StructDef
            n
            [ x
            | C.CDecl _ itms _ <- ma
            , C.CDeclarationItem (C.CDeclr (Just x) _ _ _ _) _ _ <- itms
            ]
            (C.CStruct a (Just n) (Just ma) b c)
        ]
      _ow -> []
    
    
    chrg's avatar
    chrg committed
    trySplit :: (MonadReduce l m, Eq a) => l -> a -> (a -> a) -> m a
    trySplit l a action = do
      let a' = action a
      if a /= a'
        then split l (pure a') (pure a)
        else pure a
    
    
    chrg's avatar
    chrg committed
    reduceCFunDef
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m, HasCallStack)
    
    chrg's avatar
    chrg committed
      => C.CFunctionDef C.NodeInfo
      -> Context
      -> m (C.CFunctionDef C.NodeInfo)
    reduceCFunDef (C.CFunDef spc dec cdecls smt ni) ctx = do
    
    chrg's avatar
    chrg committed
      spc1 <- trySplit ("remove static", C.posOf ni) spc $ filter \case
        C.CStorageSpec (C.CStatic _) -> False
        _ow -> True
      spc2 <- trySplit ("remove inline", C.posOf ni) spc1 $ filter \case
        C.CFunSpec (C.CInlineQual _) -> False
        _ow -> True
    
    chrg's avatar
    chrg committed
      smt' <- reduceCStatementOrEmptyBlock smt ctx'
      pure $
        C.CFunDef
    
    chrg's avatar
    chrg committed
          (inlineTypeDefsSpecs spc2 ctx)
    
    chrg's avatar
    chrg committed
          (inlineTypeDefsCDeclarator dec ctx)
          (map (`inlineTypeDefsCDeclaration` ctx) cdecls)
    
    chrg's avatar
    chrg committed
          smt'
          ni
     where
    
    chrg's avatar
    chrg committed
      !ctx' = foldr (`addInlineExpr` IEKeep) ctx ids
      ids = params dec
    
    params :: C.CDeclarator C.NodeInfo -> [C.Ident]
    
    chrg's avatar
    chrg committed
    params (C.CDeclr _ declrs _ _ _) =
      declrs & concatMap \case
        C.CFunDeclr (C.CFunParamsNew decls _) _ _ ->
          decls & concatMap \case
            C.CDecl _ items _ ->
              items & concatMap \case
                C.CDeclarationItem (C.CDeclr (Just idx) _ _ _ _) _ _ -> [idx]
                _ow -> []
            a -> don'tHandleWithPos a
        _ow -> []
    
    chrg's avatar
    chrg committed
    
    reduceCCompoundBlockItem
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m, HasCallStack)
    
    chrg's avatar
    chrg committed
      => C.CCompoundBlockItem C.NodeInfo
      -> (Context -> m [C.CCompoundBlockItem C.NodeInfo])
      -> Context
      -> m [C.CCompoundBlockItem C.NodeInfo]
    reduceCCompoundBlockItem r cont ctx = do
      case r of
        C.CBlockStmt smt -> do
          case reduceCStatement smt ctx of
            Just rsmt -> split ("remove statement", C.posOf r) (cont ctx) do
              smt' <- rsmt
              case smt' of
                C.CCompound [] ss _ -> do
                  split ("expand compound statment", C.posOf r) ((ss <>) <$> cont ctx) do
                    (C.CBlockStmt smt' :) <$> cont ctx
                _ow -> do
                  (C.CBlockStmt smt' :) <$> cont ctx
            Nothing -> cont ctx
        C.CBlockDecl declr -> do
    
          (declr', ctx') <- handleDecl declr ctx
          case declr' of
    
    chrg's avatar
    chrg committed
            Just d -> do
              d' <- (C.CBlockDecl <$> d)
              (d' :) <$> cont ctx'
    
            Nothing -> cont ctx'
    
    chrg's avatar
    chrg committed
        a -> don'tHandle a
    
    chrg's avatar
    chrg committed
    
    
    handleDecl
      :: (MonadReduce Lab m)
      => C.CDeclaration C.NodeInfo
      -> Context
    
    chrg's avatar
    chrg committed
      -> m (Maybe (m (C.CDeclaration C.NodeInfo)), Context)
    
    handleDecl d ctx = case inlineTypeDefsCDeclaration d ctx of
      -- A typedef
      C.CDecl (C.CStorageSpec (C.CTypedef _) : rst) decl _ -> do
        let [ids] = identifiers decl
    
    chrg's avatar
    chrg committed
        whenSplit
          (InlineTypeDefs `isIn` ctx)
    
          ("inline typedef " <> C.identToString ids, C.posOf d)
          (pure (Nothing, addTypeDefs [ids] (ITInline rst) ctx))
    
    chrg's avatar
    chrg committed
          (pure (Just (pure d), addTypeDefs [ids] ITKeep ctx))
    
    chrg's avatar
    chrg committed
      C.CDecl spc decl ni' -> do
    
        (decl', ctx') <- foldr reduceCDeclarationItem (pure ([], ctx)) decl
    
    chrg's avatar
    chrg committed
        let fn = do
              spc1 <- trySplit ("remove static", C.posOf ni') spc $ filter \case
                C.CStorageSpec (C.CStatic _) -> False
                _ow -> True
    
              pure $ C.CDecl spc1 decl' ni'
    
    chrg's avatar
    chrg committed
        case (decl', structIds spc) of
    
          ([], [])
            | AllowEmptyDeclarations `isIn` ctx' ->
                split ("remove empty declaration", C.posOf d) (pure (Nothing, ctx')) do
    
    chrg's avatar
    chrg committed
                  pure (Just fn, ctx')
    
            | otherwise -> pure (Nothing, ctx')
          ([], stcts) ->
            split
              ("remove declaration", C.posOf d)
              (pure (Nothing, foldr removeStruct ctx' stcts))
              do
    
    chrg's avatar
    chrg committed
                pure (Just fn, foldr addStruct ctx' stcts)
    
    chrg's avatar
    chrg committed
            pure (Just fn, foldr addStruct ctx' stcts)
    
      a -> don'tHandleWithPos a
    
    chrg's avatar
    chrg committed
    reduceCDeclarationItem
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m)
    
    chrg's avatar
    chrg committed
      => C.CDeclarationItem C.NodeInfo
    
    chrg's avatar
    chrg committed
      -> m ([C.CDeclarationItem C.NodeInfo], Context)
      -> m ([C.CDeclarationItem C.NodeInfo], Context)
    
    chrg's avatar
    chrg committed
    reduceCDeclarationItem d ma = case d of
      C.CDeclarationItem
    
    chrg's avatar
    chrg committed
        dr@(C.CDeclr (Just i) [] Nothing [] ni)
        (Just (C.CInitExpr c ni'))
    
    chrg's avatar
    chrg committed
        Nothing -> do
    
    chrg's avatar
    chrg committed
          (ds, ctx) <- ma
          c' <- fromMaybe (pure zeroExpr) (reduceCExpr c ctx)
    
    chrg's avatar
    chrg committed
          split
            ("inline variable " <> C.identToString i, C.posOf ni)
    
    chrg's avatar
    chrg committed
            (pure (ds, addInlineExpr i (IEInline c') ctx))
    
    chrg's avatar
    chrg committed
            ( pure
    
    chrg's avatar
    chrg committed
                ( inlineTypeDefsCDI (C.CDeclarationItem dr (Just (C.CInitExpr c' ni')) Nothing) ctx
                    : ds
    
    chrg's avatar
    chrg committed
                , addInlineExpr i IEKeep ctx
    
    chrg's avatar
    chrg committed
                )
            )
    
    chrg's avatar
    chrg committed
      C.CDeclarationItem (C.CDeclr (Just i) a Nothing b ni) ex Nothing -> do
    
    chrg's avatar
    chrg committed
        (ds, ctx) <- ma
    
    chrg's avatar
    chrg committed
        ex' <- case ex of
          Just ix -> maybeSplit ("remove initializer", C.posOf ni) (reduceCInitializer ix ctx)
          Nothing -> pure Nothing
        let d' = C.CDeclarationItem (C.CDeclr (Just i) a Nothing b ni) ex' Nothing
    
    chrg's avatar
    chrg committed
        split
          ("remove variable " <> C.identToString i, C.posOf ni)
    
    chrg's avatar
    chrg committed
          (pure (ds, addInlineExpr i IEDelete ctx))
    
    chrg's avatar
    chrg committed
          (pure (inlineTypeDefsCDI d' ctx : ds, addInlineExpr i IEKeep ctx))
    
    chrg's avatar
    chrg committed
      a@(C.CDeclarationItem (C.CDeclr _ _ _ _ ni) _ _) -> do
        don'tHandleWithNodeInfo a ni
    
    chrg's avatar
    chrg committed
      a -> don'tHandle a
    
    
    chrg's avatar
    chrg committed
    reduceCInitializer
      :: (MonadReduce Lab m)
      => C.CInitializer C.NodeInfo
      -> Context
      -> Maybe (m (C.CInitializer C.NodeInfo))
    reduceCInitializer a ctx = case a of
      C.CInitExpr e ni' -> do
        rm <- reduceCExpr e ctx
        Just $ (`C.CInitExpr` ni') <$> rm
      C.CInitList (C.CInitializerList items) ni -> do
        ritems <- forM items \case
          ([], it) -> fmap ([],) <$> reduceCInitializer it ctx
          (as, _) -> notSupportedYet (fmap noinfo as) ni
        Just $ (`C.CInitList` ni) . C.CInitializerList <$> sequence ritems
    
    
    chrg's avatar
    chrg committed
    reduceCStatementOrEmptyBlock
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m, HasCallStack)
    
    chrg's avatar
    chrg committed
      => C.CStatement C.NodeInfo
      -> Context
      -> m (C.CStatement C.NodeInfo)
    reduceCStatementOrEmptyBlock stmt ctx = do
      case reduceCStatement stmt ctx of
        Just ex -> do
          ex
        Nothing -> do
          pure emptyBlock
    
    chrg's avatar
    chrg committed
    
    emptyBlock :: C.CStatement C.NodeInfo
    emptyBlock = C.CCompound [] [] C.undefNode
    
    chrg's avatar
    chrg committed
    
    reduceCStatement
    
    chrg's avatar
    chrg committed
      :: (MonadReduce Lab m, HasCallStack)
    
    chrg's avatar
    chrg committed
      => C.CStatement C.NodeInfo
      -> Context
      -> Maybe (m (C.CStatement C.NodeInfo))
    reduceCStatement smt ctx = case smt of
      C.CCompound is cbi ni -> Just do
        cbi' <- foldr reduceCCompoundBlockItem (\_ -> pure []) cbi ctx
        pure $ C.CCompound is cbi' ni
      C.CWhile e s dow ni -> do
        rs <- reduceCStatement s ctx
        Just do
          e' <- reduceCExprOrZero e ctx
          s' <- rs
          pure $ C.CWhile e' s' dow ni
      C.CExpr me ni -> do
        case me of
          Just e -> do
            if DoNoops `isIn` ctx
              then Just do
                e' <- maybeSplit ("change to noop", C.posOf smt) $ reduceCExpr e ctx
                pure $ C.CExpr e' ni
              else do
                re <- reduceCExpr e ctx
                Just do
                  e' <- re
                  pure $ C.CExpr (Just e') ni
          Nothing ->
            Just $ pure $ C.CExpr Nothing ni
    
    chrg's avatar
    chrg committed
      C.CReturn me ni -> do
        -- TODO: If function returntype is not struct return 0
    
    chrg's avatar
    chrg committed
        case me of
          Just e -> do
    
    chrg's avatar
    chrg committed
            re <- reduceCExpr e ctx
            Just $ do
              e' <- re
              pure $ C.CReturn (Just e') ni
    
    chrg's avatar
    chrg committed
          Nothing ->
    
    chrg's avatar
    chrg committed
            Just . pure $ C.CReturn Nothing ni
    
    chrg's avatar
    chrg committed
      C.CIf e s els ni -> Just do
        e' <- maybeSplit ("remove condition", C.posOf e) $ reduceCExpr e ctx
        els' <- case els of
          Just els' -> do
            maybeSplit ("remove else branch", C.posOf els') do
              reduceCStatement els' ctx
          Nothing -> pure Nothing
    
    chrg's avatar
    chrg committed
        ms' <- maybeSplit ("remove if branch", C.posOf s) do
          reduceCStatement s ctx
        case (e', ms', els') of
          (Nothing, Nothing, Nothing) -> pure emptyBlock
          (Just e'', Just s', Nothing) -> pure $ C.CIf e'' s' Nothing ni
          (Nothing, Just s', Just x) -> pure $ C.CIf zeroExpr s' (Just x) ni
          (Just e'', Just s', Just x) -> pure $ C.CIf e'' s' (Just x) ni
          (Just e'', Nothing, Nothing) -> pure $ C.CExpr (Just e'') C.undefNode
          (Nothing, Nothing, Just x) -> pure x
          (Just e'', Nothing, Just x) -> pure $ C.CIf e'' emptyBlock (Just x) ni
          (Nothing, Just s', Nothing) -> pure s'
    
    chrg's avatar
    chrg committed
      C.CFor e1 e2 e3 s ni -> Just $ do
        (me1', ctx') <- case e1 of
          C.CForDecl (C.CDecl rec decl ni') -> do
            (decl', ctx') <- foldr reduceCDeclarationItem (pure ([], ctx)) decl
            res <-
              if null decl'
                then
                  whenSplit
                    (AllowEmptyDeclarations `isIn` ctx')
                    ("remove empty declaration", C.posOf ni')
                    (pure Nothing)
                    (pure $ Just $ C.CForDecl (C.CDecl rec decl' ni'))
                else pure $ Just $ C.CForDecl (C.CDecl rec decl' ni')
            pure (res, ctx')
    
    chrg's avatar
    chrg committed
          C.CForInitializing e -> do
            e' <- maybeSplit ("remove initializer", C.posOf ni) (e >>= \e' -> reduceCExpr e' ctx)
    
    chrg's avatar
    chrg committed
            whenSplit
              (AllowEmptyDeclarations `isIn` ctx)
              ("remove empty declaration", C.posOf ni)
              (pure (Nothing, ctx))
    
    chrg's avatar
    chrg committed
              (pure (Just $ C.CForInitializing e', ctx))
    
    chrg's avatar
    chrg committed
          d -> don'tHandle d
    
    
    chrg's avatar
    chrg committed
        s' <- reduceCStatementOrEmptyBlock s ctx'
    
        let forloop n = do
    
    chrg's avatar
    chrg committed
              e2' <- case e2 of
                Just e2' -> maybeSplit ("remove check", C.posOf e2') (reduceCExpr e2' ctx')
                Nothing -> pure Nothing
              e3' <- case e3 of
                Just e3' -> maybeSplit ("remove iterator", C.posOf e3') (reduceCExpr e3' ctx')
                Nothing -> pure Nothing
    
              let e2'' =
                    if AllowInfiniteForLoops `isIn` ctx || isNothing e2
                      then e2'
                      else e2' <|> Just zeroExpr
              pure $ C.CFor n e2'' e3' s' ni
    
        case me1' of
          Nothing -> do
            split ("remove the for loop", C.posOf smt) (pure s') do
              forloop (C.CForInitializing Nothing)
    
    chrg's avatar
    chrg committed
          Just e1' -> do
    
            forloop e1'
    
    chrg's avatar
    chrg committed
      C.CBreak ni -> Just do
        pure (C.CBreak ni)
    
    chrg's avatar
    chrg committed
      C.CCont ni -> Just do
        pure (C.CCont ni)
    
    chrg's avatar
    chrg committed
      C.CLabel i s [] ni -> Just do
        s' <- reduceCStatementOrEmptyBlock s ctx
        pure $ C.CLabel i s' [] ni
      C.CGoto i ni -> Just do
        pure $ C.CGoto i ni
    
    chrg's avatar
    chrg committed
      a -> don'tHandleWithPos a
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    -- | If the condition is statisfied try to reduce to the a.
    whenSplit :: (MonadReduce Lab m) => Bool -> Lab -> m a -> m a -> m a
    whenSplit cn lab a b
      | cn = split lab a b
      | otherwise = b
    
    maybeSplit :: (MonadReduce Lab m) => Lab -> Maybe (m a) -> m (Maybe a)
    maybeSplit lab = \case
      Just r -> do
        split lab (pure Nothing) (Just <$> r)
      Nothing -> do
        pure Nothing
    
    
    chrg's avatar
    chrg committed
    zeroExpr :: C.CExpression C.NodeInfo
    zeroExpr = C.CConst (C.CIntConst (C.cInteger 0) C.undefNode)
    
    
    chrg's avatar
    chrg committed
    reduceCExprOrZero :: (MonadReduce Lab m, HasCallStack) => C.CExpr -> Context -> m C.CExpr
    
    chrg's avatar
    chrg committed
    reduceCExprOrZero expr ctx = do
      case reduceCExpr expr ctx of
        Just ex -> do
    
    chrg's avatar
    chrg committed
          r <- ex
          if r == zeroExpr
            then pure r
            else split ("replace by zero", C.posOf expr) (pure zeroExpr) (pure r)
    
    chrg's avatar
    chrg committed
        Nothing -> do
          pure zeroExpr
    
    chrg's avatar
    chrg committed
    {-# INLINE reduceCExprOrZero #-}
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    reduceCExpr :: (MonadReduce Lab m, HasCallStack) => C.CExpr -> Context -> Maybe (m C.CExpr)
    
    chrg's avatar
    chrg committed
    reduceCExpr expr ctx = case expr of
      C.CBinary o elhs erhs ni -> do
    
    chrg's avatar
    chrg committed
        if o `elem` [C.CNeqOp, C.CEqOp, C.CGeqOp, C.CLeqOp, C.CGrOp, C.CLeOp]
          then do
            -- in this case we change type, so we need to keep the operation
            rl <- reduceCExpr elhs ctx
            rr <- reduceCExpr erhs ctx
            Just $ do
              l' <- rl
              r' <- rr
              pure $ C.CBinary o l' r' ni
          else do
            case reduceCExpr elhs ctx of
              Just elhs' -> case reduceCExpr erhs ctx of
                Just erhs' -> pure do
                  split ("reduce to left", C.posOf elhs) elhs' do
                    split ("reduce to right", C.posOf erhs) erhs' do
                      l' <- elhs'
                      r' <- erhs'
                      pure $ C.CBinary o l' r' ni
                Nothing ->
                  pure elhs'
              Nothing
                | otherwise -> fail "could not reduce left hand side"
    
    chrg's avatar
    chrg committed
      C.CAssign o elhs erhs ni ->
        case reduceCExpr elhs (addKeyword DisallowVariableInlining ctx) of
          Just elhs' -> case reduceCExpr erhs ctx of
            Just erhs' -> pure do
              split ("reduce to left", C.posOf elhs) elhs' do
                split ("reduce to right", C.posOf erhs) erhs' do
                  l' <- elhs'
                  r' <- erhs'
                  pure $ C.CAssign o l' r' ni
            Nothing ->
              fail "could not reduce right hand side"
          Nothing
            | otherwise -> fail "could not reduce left hand side"
      C.CVar i _ ->
        case Map.lookup i . inlineExprs $ ctx of
          Just mx -> case mx of
    
    chrg's avatar
    chrg committed
            IEKeep -> Just (pure expr)
            IEInline mx'
    
    chrg's avatar
    chrg committed
              | DisallowVariableInlining `isIn` ctx -> Nothing
    
    chrg's avatar
    chrg committed
              | otherwise -> Just (pure mx')
    
    chrg's avatar
    chrg committed
            IEDelete ->
    
    chrg's avatar
    chrg committed
              Nothing
          Nothing -> error ("Could not find " <> show i <> " at " <> show (C.posOf expr) <> "\n" <> show (inlineExprs ctx))
    
    chrg's avatar
    chrg committed
      C.CConst x -> Just do
        pure $ C.CConst x
      C.CUnary o elhs ni -> do
        elhs' <- reduceCExpr elhs (addKeyword DisallowVariableInlining ctx)
        Just $ split ("reduce to operant", C.posOf expr) elhs' do
          e <- elhs'
          pure $ C.CUnary o e ni
    
      C.CCall e es ni -> do
    
    chrg's avatar
    chrg committed
        let re = reduceCExpr e (addKeyword DisallowVariableInlining ctx)
            res = map (`reduceCExpr` ctx) es
        case (re, catMaybes res) of
          (Nothing, []) -> Nothing
          (Nothing, [r]) -> Just r
          (_, _) -> Just do
            e' <- maybeSplit ("do without function", C.posOf e) re
            es' <- res & traverse (maybeSplit ("do without pram", C.posOf e))
            case (e', catMaybes es') of
              (Nothing, []) -> pure zeroExpr
              (Nothing, [e'']) -> pure e''
              (Nothing, es'') -> pure $ C.CComma es'' C.undefNode
              (Just f, _) -> pure $ C.CCall f (map (fromMaybe zeroExpr) es') ni
    
    chrg's avatar
    chrg committed
      C.CCond ec et ef ni -> do
        -- TODO: More fine grained reduction is possible here.
        Just $ do
          ec' <- reduceCExprOrZero ec ctx
          ef' <- reduceCExprOrZero ef ctx
          et' <- case et of
            Just et' -> Just <$> reduceCExprOrZero et' ctx
            Nothing -> pure Nothing
          pure $ C.CCond ec' et' ef' ni
      C.CCast decl e ni -> do
        re <- reduceCExpr e ctx
        Just do
          split ("don't cast", C.posOf ni) re do
            e' <- re
    
    chrg's avatar
    chrg committed
            pure (C.CCast (inlineTypeDefsCDeclaration decl ctx) e' ni)
    
    chrg's avatar
    chrg committed
      C.CIndex e1 e2 ni -> do
        -- TODO: Better reduction is posisble here.
        re1 <- reduceCExpr e1 ctx
        Just do
          e1' <- re1
          e2' <- reduceCExprOrZero e2 ctx
          pure $ C.CIndex e1' e2' ni
    
    chrg's avatar
    chrg committed
      C.CComma items ni -> do
    
    chrg's avatar
    chrg committed
        let Just (x, rst) = List.uncons (reverse items)
    
    chrg's avatar
    chrg committed
        rx <- reduceCExpr x ctx
        Just do
          rst' <-
            foldr
              ( \e cc -> do
                  maybeSplit ("remove expression", C.posOf e) (reduceCExpr e ctx) >>= \case
                    Just e' -> (e' :) <$> cc
                    Nothing -> cc
              )
              (pure [])
              rst
          x' <- rx
          if List.null rst'
            then pure x'
            else pure $ C.CComma (reverse (x' : rst')) ni
    
      C.CMember e i l ni -> do
        re <- reduceCExpr e ctx
        Just do
          e' <- re
          pure (C.CMember e' i l ni)
    
    chrg's avatar
    chrg committed
      a -> don'tHandleWithPos a
    
    
    chrg's avatar
    chrg committed
    inlineTypeDefsCDeclaration :: C.CDeclaration C.NodeInfo -> Context -> C.CDeclaration C.NodeInfo
    inlineTypeDefsCDeclaration decl ctx =
      case decl of
        C.CDecl items decli ni ->
          C.CDecl (inlineTypeDefsSpecs items ctx) (map (`inlineTypeDefsCDI` ctx) decli) ni
        a -> don'tHandle a
    
    chrg's avatar
    chrg committed
    
    
    chrg's avatar
    chrg committed
    inlineTypeDefsSpecs :: [C.CDeclarationSpecifier C.NodeInfo] -> Context -> [C.CDeclarationSpecifier C.NodeInfo]
    inlineTypeDefsSpecs r ctx =
      r & concatMap \case
        a@(C.CTypeSpec (C.CTypeDef idx _)) -> do
          case Map.lookup idx . typeDefs $ ctx of
            Just ITKeep -> [a]
            Just (ITInline res) -> res
            Nothing -> error ("could not find typedef:" <> show idx)
    
        a@(C.CTypeSpec (C.CSUType (C.CStruct _ (Just idx) Nothing _ _) _)) ->
          case Map.lookup idx . structs $ ctx of
            Just (Just def) -> [C.CTypeSpec (C.CSUType def C.undefNode)]
            Just Nothing -> [a]
            Nothing -> error ("could not find struct:" <> show idx)
        C.CTypeSpec (C.CSUType (C.CStruct a b (Just c) d e) f) ->
          [C.CTypeSpec (C.CSUType (C.CStruct a b (Just $ map (`inlineTypeDefsCDeclaration` ctx) c) d e) f)]
    
    chrg's avatar
    chrg committed
        a -> [a]
    {-# NOINLINE inlineTypeDefsSpecs #-}
    
    inlineTypeDefsCDeclarator
      :: C.CDeclarator C.NodeInfo
      -> Context
      -> C.CDeclarator C.NodeInfo
    inlineTypeDefsCDeclarator (C.CDeclr idn derivedd st atr ni) ctx =
    
    chrg's avatar
    chrg committed
      C.CDeclr idn (map (inlineTypeDefsX ctx) derivedd) st atr ni
    
    inlineTypeDefsX :: Context -> C.CDerivedDeclarator C.NodeInfo -> C.CDerivedDeclarator C.NodeInfo
    inlineTypeDefsX ctx = \case
      C.CFunDeclr (C.CFunParamsNew x y) b c ->
        C.CFunDeclr (C.CFunParamsNew (map (`inlineTypeDefsCDeclaration` ctx) x) y) b c
      C.CArrDeclr a b c -> C.CArrDeclr a b c
      C.CPtrDeclr a b -> C.CPtrDeclr a b
      a -> don'tHandle a
    
    chrg's avatar
    chrg committed
    inlineTypeDefsCDI :: C.CDeclarationItem C.NodeInfo -> Context -> C.CDeclarationItem C.NodeInfo
    inlineTypeDefsCDI di ctx = case di of
      C.CDeclarationItem a b ni -> C.CDeclarationItem (inlineTypeDefsCDeclarator a ctx) b ni
      a -> don'tHandle a
    
    
    identifiers :: forall a. (Data a) => a -> [C.Ident]
    
    chrg's avatar
    chrg committed
    identifiers d = appEndo (go d) []
     where
      go :: forall a'. (Data a') => a' -> Endo [C.Ident]
      go d' = case cast d' of
        Just l -> Endo (l :)
        Nothing -> gmapQl (<>) mempty go d'
    
    
    functionName :: C.CFunctionDef C.NodeInfo -> Maybe C.Ident
    functionName = \case
      C.CFunDef _ (C.CDeclr ix _ _ _ _) _ _ _ -> ix
    
    
    chrg's avatar
    chrg committed
    notSupportedYet :: (HasCallStack, Show a) => a -> C.NodeInfo -> b
    notSupportedYet a ni = error (show a <> " at " <> show (C.posOf ni))
    
    noinfo :: (Functor f) => f C.NodeInfo -> f ()
    noinfo a = a $> ()
    
    
    chrg's avatar
    chrg committed
    don'tHandle :: (HasCallStack, Functor f, Show (f ())) => f C.NodeInfo -> b
    don'tHandle f = error (show (f $> ()))
    
    
    chrg's avatar
    chrg committed
    don'tHandleWithPos :: (HasCallStack, Functor f, Show (f ()), C.Pos (f C.NodeInfo)) => f C.NodeInfo -> b
    don'tHandleWithPos f = error (show (f $> ()) <> " at " <> show (C.posOf f))
    
    don'tHandleWithNodeInfo :: (HasCallStack, Functor f, Show (f ())) => f C.NodeInfo -> C.NodeInfo -> b
    don'tHandleWithNodeInfo f ni = error (show (f $> ()) <> " at " <> show (C.posOf ni))