    -- ----------------------------------------------------------------------------
    -- | Pretty print helpers for the LLVM Code generator.
    --

    module LlvmCodeGen.Ppr (
            pprLlvmHeader, pprLlvmCmmTop, pprLlvmData, infoSection, iTableSuf
        ) where

    #include "HsVersions.h"

    import Llvm
    import LlvmCodeGen.Base
    import LlvmCodeGen.Data

    import CLabel
    import Cmm

    import FastString
    import qualified Outputable
    import Pretty
    import Unique

    -- ----------------------------------------------------------------------------
    -- * Top level
    --

    -- | LLVM module layout description for the host target
    moduleLayout :: Doc
    moduleLayout =
    #if i386_TARGET_ARCH

    #if darwin_TARGET_OS
        text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:128:128-n8:16:32\""
        $+$ text "target triple = \"i386-apple-darwin9.8\""
    #elif mingw32_TARGET_OS
        text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-f80:128:128-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32\""
        $+$ text "target triple = \"i686-pc-win32\""
    #else /* Linux */
        text "target datalayout = \"e-p:32:32:32-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:32:64-f32:32:32-f64:32:64-v64:64:64-v128:128:128-a0:0:64-f80:32:32-n8:16:32\""
        $+$ text "target triple = \"i386-pc-linux-gnu\""
    #endif

    #elif x86_64_TARGET_ARCH

    #if darwin_TARGET_OS
        text "target datalayout = \"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64\""
        $+$ text "target triple = \"x86_64-apple-darwin10.0.0\""
    #else /* Linux */
        text "target datalayout = \"e-p:64:64:64-i1:8:8-i8:8:8-i16:16:16-i32:32:32-i64:64:64-f32:32:32-f64:64:64-v64:64:64-v128:128:128-a0:0:64-s0:64:64-f80:128:128-n8:16:32:64\""
        $+$ text "target triple = \"x86_64-linux-gnu\""
    #endif

    #else /* Not x86 */
        -- FIX: Other targets
        empty
    #endif

    -- | Header code for LLVM modules
    pprLlvmHeader :: Doc
    pprLlvmHeader = moduleLayout

    -- | Pretty print LLVM data code
    pprLlvmData :: LlvmData -> Doc
    pprLlvmData (globals, types) =
        let tryConst (v, Just s )   = ppLlvmGlobal (v, Just s)
            tryConst g@(_, Nothing) = ppLlvmGlobal g

            ppLlvmTys (LMAlias    a) = ppLlvmAlias a
            ppLlvmTys (LMFunction f) = ppLlvmFunctionDecl f
            ppLlvmTys _other         = empty

            types'   = vcat $ map ppLlvmTys types
            globals' = vcat $ map tryConst globals
        in types' $+$ globals'

    -- | Pretty print LLVM code
    pprLlvmCmmTop :: LlvmEnv -> Int -> LlvmCmmTop -> (Doc, [LlvmVar])
    pprLlvmCmmTop _ _ (CmmData _ lmdata)
      = (vcat $ map pprLlvmData lmdata, [])

    pprLlvmCmmTop env count (CmmProc info lbl _ (ListGraph blks))
      = let static = CmmDataLabel lbl : info
            (idoc, ivar) = if not (null info)
                              then pprInfoTable env count lbl static
                              else (empty, [])
        in (idoc $+$ (
            let sec = mkLayoutSection (count + 1)
                (lbl',sec') = if not (null info)
                                then (entryLblToInfoLbl lbl, sec)
                                else (lbl, Nothing)
                link = if externallyVisibleCLabel lbl'
                          then ExternallyVisible
                          else Internal
                lmblocks = map (\(BasicBlock id stmts) ->
                                    LlvmBlock (getUnique id) stmts) blks
                fun = mkLlvmFunc lbl' link  sec' lmblocks
            in ppLlvmFunction fun
        ), ivar)

    -- | Pretty print CmmStatic
    pprInfoTable :: LlvmEnv -> Int -> CLabel -> [CmmStatic] -> (Doc, [LlvmVar])
    pprInfoTable env count lbl stat
      = let unres = genLlvmData (Text, stat)
            (_, (ldata, ltypes)) = resolveLlvmData env unres

            setSection ((LMGlobalVar _ ty l _ _ c), d)
                = let sec = mkLayoutSection count
                      ilabel = strCLabel_llvm (entryLblToInfoLbl lbl)
                                  `appendFS` fsLit iTableSuf
                      gv = LMGlobalVar ilabel ty l sec llvmInfAlign c
                      v = if l == Internal then [gv] else []
                  in ((gv, d), v)
            setSection v = (v,[])

            (ldata', llvmUsed) = setSection (last ldata)
        in if length ldata /= 1
              then Outputable.panic "LlvmCodeGen.Ppr: invalid info table!"
              else (pprLlvmData ([ldata'], ltypes), llvmUsed)

    -- | We generate labels for info tables by converting them to the same label
    -- as for the entry code but adding this string as a suffix.
    iTableSuf :: String
    iTableSuf = "_itable"

    -- | Create an appropriate section declaration for subsection <n> of text
    -- WARNING: This technique could fail as gas documentation says it only
    -- supports up to 8192 subsections per section. Inspection of the source
    -- code and some test programs seem to suggest it supports more than this
    -- so we are hoping it does.
    mkLayoutSection :: Int -> LMSection
    mkLayoutSection n
      -- On OSX we can't use the GNU Assembler, we must use the OSX assembler, which
      -- doesn't support subsections. So we post process the assembly code, this
      -- section specifier will be replaced with '.text' by the mangler.
      = Just (fsLit $ infoSection ++ show n
    #if darwin_TARGET_OS
          )
    #else
          ++ "#")
    #endif

    -- | The section we are putting info tables and their entry code into
    infoSection :: String
    #if darwin_TARGET_OS
    infoSection = "__STRIP,__me"
    #else
    infoSection = ".text; .text "
    #endif