{-# OPTIONS_HADDOCK hide #-}
{-# OPTIONS_GHC -fno-warn-orphans #-}
module Data.LLVM.Types (
  Module(..),
  moduleGlobals,
  findFunctionByName,
  findMain,
  module Data.LLVM.Types.Attributes,
  module Data.LLVM.Types.Dwarf,
  module Data.LLVM.Types.Identifiers,
  module Data.LLVM.Types.Referential,
  module Data.LLVM.Internal.DataLayout
  ) where

import Control.DeepSeq
import Control.Monad.Trans.State.Strict
import qualified Data.HashSet as S
import Data.List ( find, intersperse )
import Data.Monoid
import Data.Text ( Text )
import qualified Data.Text as T
import Data.Text.Lazy ( toStrict )
import Data.Text.Lazy.Builder

import Data.LLVM.Internal.ForceModule
import Data.LLVM.Internal.Printers
import Data.LLVM.Internal.DataLayout
import Data.LLVM.Types.Attributes
import Data.LLVM.Types.Dwarf
import Data.LLVM.Types.Identifiers
import Data.LLVM.Types.Referential

-- | This is the top-level representation of a program in LLVM.  This
-- is the type returned from all of the parsers, and all analysis
-- begins at the Module level.
data Module = Module { moduleIdentifier :: Text
                     , moduleDataLayout :: DataLayout
                     , moduleDataLayoutString :: Text
                       -- ^ The layout of the primitive datatypes on
                       -- the architecture this module was generated
                       -- for
                     , moduleTarget :: Text -- TargetTriple
                       -- ^ The architecture that this module was
                       -- generated for
                     , moduleAssembly :: Assembly
                       -- ^ Module-level assembly declarations
                     , moduleDefinedFunctions :: [Function]
                     , moduleGlobalVariables :: [GlobalVariable]
                     , moduleExternalValues :: [ExternalValue]
                     , moduleExternalFunctions :: [ExternalFunction]
                     , moduleAliases :: [GlobalAlias]
                     , moduleEnumMetadata :: [Metadata]
                       -- ^ All enumerations in the Module.  These
                       -- should provide a bit more information than
                       -- just combing the types appearing in
                       -- interfaces.
                     , moduleRetainedTypeMetadata :: [Metadata]
                       -- ^ The retained types in the Module.  Only
                       -- available with new-style metadata.
                     , moduleRetainedTypes :: [Type]
                       -- ^ All of the IR-level types retained by the
                       -- module.
                     , moduleNextId :: UniqueId
                     , moduleTypeSizes :: Type -> Maybe Int
                     }

-- | Implementation of the Show instance
--
-- FIXME: Print out the external values and functions
printModule :: Module -> Builder
printModule Module { moduleIdentifier = _
                   , moduleDataLayoutString = layout
                   , moduleTarget = triple
                   , moduleAssembly = asm
                   , moduleAliases = aliases
                   , moduleGlobalVariables = vars
                   , moduleDefinedFunctions = funcs
                   , moduleExternalValues = _ -- evars
                   , moduleExternalFunctions = _ -- efuncs
                   } =
  mconcat [ layoutS, singleton '\n', tripleS, singleton '\n', asmS, singleton '\n'
          , aliasesS, singleton '\n', varS, fromString "\n\n", funcS, singleton '\n'
          ]
  where
    layoutS = mconcat [ fromString "target datalayout = \""
                      , fromString (show layout), singleton '"'
                      ]
    tripleS = mconcat [ fromString "target triple = \"", fromString (show triple), singleton '"' ]
    asmS = printAsm asm
    aliasesS = mconcat $ intersperse (fromString "\n") $ map (printValue . toValue) aliases
    varS = mconcat $ intersperse (fromString "\n") $ map (printValue . toValue) vars
    funcS = mconcat $ intersperse (fromString "\n\n") $ map (printValue . toValue) funcs

-- | Get a list of all types of globals in the Module (functions,
-- aliases, and global variables)
moduleGlobals :: Module -> [Value]
moduleGlobals m = concat [ map toValue $ moduleAliases m
                         , map toValue $ moduleGlobalVariables m
                         , map toValue $ moduleDefinedFunctions m
                         , map toValue $ moduleExternalValues m
                         , map toValue $ moduleExternalFunctions m
                         ]

instance Show Module where
  show = T.unpack . toStrict . toLazyText . printModule

instance NFData Module where
  rnf m = evalState (forceModule m) (S.empty, S.empty) `seq` ()

-- | Force the module to be fully evaluated to rnf form from the
-- top-down.  There are cycles, so we have to be careful to avoid
-- traversing them infinitely.
forceModule :: Module -> ForceMonad Module
forceModule m = do
  mapM_ forceGlobalAlias (moduleAliases m)
  mapM_ forceGlobalVariable (moduleGlobalVariables m)
  mapM_ forceFunction (moduleDefinedFunctions m)
  mapM_ forceExternalValue (moduleExternalValues m)
  mapM_ forceExternalFunction (moduleExternalFunctions m)
  mapM_ forceMetadataT (moduleEnumMetadata m)
  mapM_ forceMetadataT (moduleRetainedTypeMetadata m)
  return $ moduleAssembly m `deepseq` m `seq` m

-- | Find a function in the Module by its name.
findFunctionByName :: Module -> String -> Maybe Function
findFunctionByName m s = find isFunc $ moduleDefinedFunctions m
  where
    funcIdent = makeGlobalIdentifier (T.pack s)
    isFunc f = functionName f == funcIdent

-- | Find the function named 'main' in the 'Module', if any.
findMain :: Module -> Maybe Function
findMain m = findFunctionByName m "main"