    {-# LANGUAGE CPP #-}

    -- ----------------------------------------------------------------------------
    -- | Pretty print helpers for the LLVM Code generator.
    --
    module LlvmCodeGen.Ppr (
            pprLlvmHeader, pprLlvmCmmDecl, pprLlvmData, infoSection
        ) where

    #include "HsVersions.h"

    import Llvm
    import LlvmCodeGen.Base
    import LlvmCodeGen.Data

    import CLabel
    import Cmm
    import Platform

    import FastString
    import Outputable
    import Unique

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

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

    -- | LLVM module layout description for the host target
    moduleLayout :: SDoc
    moduleLayout = sdocWithPlatform $ \platform ->
        case platform of
        Platform { platformArch = ArchX86, platformOS = OSDarwin } ->
            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\""
        Platform { platformArch = ArchX86, platformOS = OSMinGW32 } ->
            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\""
        Platform { platformArch = ArchX86, platformOS = OSLinux } ->
            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\""
        Platform { platformArch = ArchX86_64, platformOS = OSDarwin } ->
            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\""
        Platform { platformArch = ArchX86_64, platformOS = OSLinux } ->
            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\""
        Platform { platformArch = ArchARM {}, platformOS = OSLinux } ->
            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-v64:64:64-v128:64:128-a0:0:64-n32\""
            $+$ text "target triple = \"armv6-unknown-linux-gnueabihf\""
        Platform { platformArch = ArchARM {}, platformOS = OSAndroid } ->
            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-v64:64:64-v128:64:128-a0:0:64-n32\""
            $+$ text "target triple = \"arm-unknown-linux-androideabi\""
        Platform { platformArch = ArchARM {}, platformOS = OSQNXNTO } ->
            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-v64:64:64-v128:64:128-a0:0:64-n32\""
            $+$ text "target triple = \"arm-unknown-nto-qnx8.0.0eabi\""
        Platform { platformArch = ArchARM {}, platformOS = OSiOS } ->
            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-v64:64:64-v128:64:128-a0:0:64-n32\""
            $+$ text "target triple = \"arm-apple-darwin10\""
        Platform { platformArch = ArchX86, platformOS = OSiOS } ->
            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-darwin11\""
        Platform { platformArch = ArchARM64, platformOS = OSiOS } ->
            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-n32:64-S128\""
            $+$ text "target triple = \"arm64-apple-ios7.0.0\""
        Platform { platformArch = ArchARM64, platformOS = OSLinux } ->
            text "target datalayout = \"e-m:e-i64:64-i128:128-n32:64-S128\""
            $+$ text "target triple = \"aarch64-unknown-linux-gnu\""
        _ ->
            if platformIsCrossCompiling platform
                then panic "LlvmCodeGen.Ppr: Cross compiling without valid target info."
                else empty
            -- If you see the above panic, GHC is missing the required target datalayout
            -- and triple information. You can obtain this info by compiling a simple
            -- 'hello world' C program with the clang C compiler eg:
            --     clang hello.c -emit-llvm -o hello.ll
            -- and the first two lines of hello.ll should provide the 'target datalayout'
            -- and 'target triple' lines required.

    -- | Pretty print LLVM data code
    pprLlvmData :: LlvmData -> SDoc
    pprLlvmData (globals, types) =
        let ppLlvmTys (LMAlias    a) = ppLlvmAlias a
            ppLlvmTys (LMFunction f) = ppLlvmFunctionDecl f
            ppLlvmTys _other         = empty

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

    -- | Pretty print LLVM code
    pprLlvmCmmDecl :: LlvmCmmDecl -> LlvmM (SDoc, [LlvmVar])
    pprLlvmCmmDecl (CmmData _ lmdata)
      = return (vcat $ map pprLlvmData lmdata, [])

    pprLlvmCmmDecl (CmmProc mb_info entry_lbl live (ListGraph blks))
      = do let lbl = case mb_info of
                         Nothing                   -> entry_lbl
                         Just (Statics info_lbl _) -> info_lbl
               link = if externallyVisibleCLabel lbl
                          then ExternallyVisible
                          else Internal
               lmblocks = map (\(BasicBlock id stmts) ->
                                    LlvmBlock (getUnique id) stmts) blks

           funDec <- llvmFunSig live lbl link
           dflags <- getDynFlags
           let buildArg = fsLit . showSDoc dflags . ppPlainName
               funArgs = map buildArg (llvmFunArgs dflags live)
               funSect = llvmFunSection dflags (decName funDec)

           -- generate the info table
           prefix <- case mb_info of
                         Nothing -> return Nothing
                         Just (Statics _ statics) -> do
                           infoStatics <- mapM genData statics
                           let infoTy = LMStruct $ map getStatType infoStatics
                           return $ Just $ LMStaticStruc infoStatics infoTy

           let fun = LlvmFunction funDec funArgs llvmStdFunAttrs funSect
                                  prefix lmblocks
               name = decName $ funcDecl fun
               defName = name `appendFS` fsLit "$def"
               funcDecl' = (funcDecl fun) { decName = defName }
               fun' = fun { funcDecl = funcDecl' }
               funTy = LMFunction funcDecl'
               funVar = LMGlobalVar name
                                    (LMPointer funTy)
                                    link
                                    Nothing
                                    Nothing
                                    Alias
               defVar = LMGlobalVar defName
                                    (LMPointer funTy)
                                    (funcLinkage funcDecl')
                                    (funcSect fun)
                                    (funcAlign funcDecl')
                                    Alias
               alias = LMGlobal funVar
                                (Just $ LMBitc (LMStaticPointer defVar)
                                               (LMPointer $ LMInt 8))

           return (ppLlvmGlobal alias $+$ ppLlvmFunction fun', [])

    -- | The section we are putting info tables and their entry code into, should
    -- be unique since we process the assembly pattern matching this.
    infoSection :: String
    infoSection = "X98A__STRIP,__me"