    {-# LANGUAGE MagicHash #-}

    -----------------------------------------------------------------------------
    --
    -- Pretty-printing assembly language
    --
    -- (c) The University of Glasgow 1993-2005
    --
    -----------------------------------------------------------------------------

    module GHC.CmmToAsm.Ppr (
            doubleToBytes,
            floatToBytes,
            pprASCII,
            pprString,
            pprFileEmbed,
            pprSectionHeader
    )

    where

    import GHC.Prelude

    import GHC.Utils.Asm
    import GHC.Cmm.CLabel
    import GHC.Cmm
    import GHC.CmmToAsm.Config
    import GHC.Utils.Outputable as SDoc
    import GHC.Utils.Panic
    import GHC.Platform

    import qualified Data.Array.Unsafe as U ( castSTUArray )
    import Data.Array.ST

    import Control.Monad.ST
    import Data.Word
    import Data.ByteString (ByteString)
    import qualified Data.ByteString as BS
    import GHC.Exts
    import GHC.Word

    -- -----------------------------------------------------------------------------
    -- Converting floating-point literals to integrals for printing

    -- | Get bytes of a Float representation
    floatToBytes :: Float -> [Word8]
    floatToBytes :: Float -> [Word8]
    floatToBytes Float
    f = (forall s. ST s [Word8]) -> [Word8]
    forall a. (forall s. ST s a) -> a
    runST ((forall s. ST s [Word8]) -> [Word8])
    -> (forall s. ST s [Word8]) -> [Word8]
    forall a b. (a -> b) -> a -> b
    $ do
      arr <- (Int, Int) -> ST s (STUArray s Int Float)
    forall i. Ix i => (i, i) -> ST s (STUArray s i Float)
    forall (a :: * -> * -> *) e (m :: * -> *) i.
    (MArray a e m, Ix i) =>
    (i, i) -> m (a i e)
    newArray_ ((Int
    0::Int),Int
    3)
      writeArray arr 0 f
      let cast :: STUArray s Int Float -> ST s (STUArray s Int Word8)
          cast = STUArray s Int Float -> ST s (STUArray s Int Word8)
    forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
    U.castSTUArray
      arr <- cast arr
      i0 <- readArray arr 0
      i1 <- readArray arr 1
      i2 <- readArray arr 2
      i3 <- readArray arr 3
      return [i0,i1,i2,i3]

    -- | Get bytes of a Double representation
    doubleToBytes :: Double -> [Word8]
    doubleToBytes :: Double -> [Word8]
    doubleToBytes Double
    d = (forall s. ST s [Word8]) -> [Word8]
    forall a. (forall s. ST s a) -> a
    runST ((forall s. ST s [Word8]) -> [Word8])
    -> (forall s. ST s [Word8]) -> [Word8]
    forall a b. (a -> b) -> a -> b
    $ do
      arr <- (Int, Int) -> ST s (STUArray s Int Double)
    forall i. Ix i => (i, i) -> ST s (STUArray s i Double)
    forall (a :: * -> * -> *) e (m :: * -> *) i.
    (MArray a e m, Ix i) =>
    (i, i) -> m (a i e)
    newArray_ ((Int
    0::Int),Int
    7)
      writeArray arr 0 d
      let cast :: STUArray s Int Double -> ST s (STUArray s Int Word8)
          cast = STUArray s Int Double -> ST s (STUArray s Int Word8)
    forall s ix a b. STUArray s ix a -> ST s (STUArray s ix b)
    U.castSTUArray
      arr <- cast arr
      i0 <- readArray arr 0
      i1 <- readArray arr 1
      i2 <- readArray arr 2
      i3 <- readArray arr 3
      i4 <- readArray arr 4
      i5 <- readArray arr 5
      i6 <- readArray arr 6
      i7 <- readArray arr 7
      return [i0,i1,i2,i3,i4,i5,i6,i7]

    -- ---------------------------------------------------------------------------
    -- Printing ASCII strings.
    --
    -- Print as a string and escape non-printable characters.
    -- This is similar to charToC in GHC.Utils.Misc

    pprASCII :: forall doc. IsLine doc => ByteString -> doc
    pprASCII :: forall doc. IsLine doc => ByteString -> doc
    pprASCII ByteString
    str
      -- Transform this given literal bytestring to escaped string and construct
      -- the literal SDoc directly.
      -- See #14741
      -- and Note [Pretty print ASCII when AsmCodeGen]
      --
      -- We work with a `Doc` instead of an `SDoc` because there is no need to carry
      -- an `SDocContext` that we don't use. It leads to nicer (STG) code.
      = (Word8 -> doc -> doc) -> doc -> ByteString -> doc
    forall a. (Word8 -> a -> a) -> a -> ByteString -> a
    BS.foldr Word8 -> doc -> doc
    f doc
    forall doc. IsOutput doc => doc
    empty ByteString
    str
        where
           f :: Word8 -> doc -> doc
           f :: Word8 -> doc -> doc
    f Word8
    w doc
    s = Word8 -> doc
    do1 Word8
    w doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> doc
    s

           do1 :: Word8 -> doc
           do1 :: Word8 -> doc
    do1 Word8
    w | Word8
    0x09 Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    w = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\\t"
                 | Word8
    0x0A Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    w = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\\n"
                 | Word8
    0x22 Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    w = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\\\""
                 | Word8
    0x5C Word8 -> Word8 -> Bool
    forall a. Eq a => a -> a -> Bool
    == Word8
    w = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\\\\"
                   -- ASCII printable characters range
                 | Word8
    w Word8 -> Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    >= Word8
    0x20 Bool -> Bool -> Bool
    && Word8
    w Word8 -> Word8 -> Bool
    forall a. Ord a => a -> a -> Bool
    <= Word8
    0x7E = Char -> doc
    forall doc. IsLine doc => Char -> doc
    char (Word8 -> Char
    chr' Word8
    w)
                 | Bool
    otherwise = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    xs
                    where
                     !xs :: String
    xs = [ Char
    '\\', Char
    x0, Char
    x1, Char
    x2] -- octal
                     !x0 :: Char
    x0 = Word8 -> Char
    chr' (Word8
    ord0 Word8 -> Word8 -> Word8
    forall a. Num a => a -> a -> a
    + (Word8
    w Word8 -> Int -> Word8
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    6) Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    .&. Word8
    0x07)
                     !x1 :: Char
    x1 = Word8 -> Char
    chr' (Word8
    ord0 Word8 -> Word8 -> Word8
    forall a. Num a => a -> a -> a
    + (Word8
    w Word8 -> Int -> Word8
    forall a. Bits a => a -> Int -> a
    `unsafeShiftR` Int
    3) Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    .&. Word8
    0x07)
                     !x2 :: Char
    x2 = Word8 -> Char
    chr' (Word8
    ord0 Word8 -> Word8 -> Word8
    forall a. Num a => a -> a -> a
    + Word8
    w Word8 -> Word8 -> Word8
    forall a. Bits a => a -> a -> a
    .&. Word8
    0x07)
                     !ord0 :: Word8
    ord0 = Word8
    0x30 -- = ord '0'

           -- we know that the Chars we create are in the ASCII range
           -- so we bypass the check in "chr"
           chr' :: Word8 -> Char
           chr' :: Word8 -> Char
    chr' (W8# Word8#
    w#) = Char# -> Char
    C# (Int# -> Char#
    chr# (Word# -> Int#
    word2Int# (Word8# -> Word#
    word8ToWord# Word8#
    w#)))
    {-# SPECIALIZE pprASCII :: ByteString -> SDoc #-}
    {-# SPECIALIZE pprASCII :: ByteString -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    -- | Emit a ".string" directive
    pprString :: IsLine doc => ByteString -> doc
    pprString :: forall doc. IsLine doc => ByteString -> doc
    pprString ByteString
    bs = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\t.string " doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> doc -> doc
    forall doc. IsLine doc => doc -> doc
    doubleQuotes (ByteString -> doc
    forall doc. IsLine doc => ByteString -> doc
    pprASCII ByteString
    bs)
    {-# SPECIALIZE pprString :: ByteString -> SDoc #-}
    {-# SPECIALIZE pprString :: ByteString -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    -- | Emit a ".incbin" directive
    --
    -- A NULL byte is added after the binary data.
    pprFileEmbed :: IsLine doc => FilePath -> doc
    pprFileEmbed :: forall doc. IsLine doc => String -> doc
    pprFileEmbed String
    path
       = String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\t.incbin "
         doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> String -> doc
    forall doc. IsLine doc => String -> doc
    pprFilePathString String
    path -- proper escape (see #16389)
         doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    "\n\t.byte 0"
    {-# SPECIALIZE pprFileEmbed :: FilePath -> SDoc #-}
    {-# SPECIALIZE pprFileEmbed :: FilePath -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    {-
    Note [Embedding large binary blobs]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

    To embed a blob of binary data (e.g. an UTF-8 encoded string) into the generated
    code object, we have several options:

       1. Generate a ".byte" directive for each byte. This is what was done in the past
          (see Note [Pretty print ASCII when AsmCodeGen]).

       2. Generate a single ".string"/".asciz" directive for the whole sequence of
          bytes. Bytes in the ASCII printable range are rendered as characters and
          other values are escaped (e.g., "\t", "\077", etc.).

       3. Create a temporary file into which we dump the binary data and generate a
          single ".incbin" directive. The assembler will include the binary file for
          us in the generated output object.

    Now the code generator uses either (2) or (3), depending on the binary blob
    size.  Using (3) for small blobs adds too much overhead (see benchmark results
    in #16190), so we only do it when the size is above a threshold (500K at the
    time of writing).

    The threshold is configurable via the `-fbinary-blob-threshold` flag.

    -}

    {-
    Note [Pretty print ASCII when AsmCodeGen]
    ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    Previously, when generating assembly code, we created SDoc with
    `(ptext . sLit)` for every bytes in literal bytestring, then
    combine them using `hcat`.

    When handling literal bytestrings with millions of bytes,
    millions of SDoc would be created and to combine, leading to
    high memory usage.

    Now we escape the given bytestring to string directly and construct
    SDoc only once. This improvement could dramatically decrease the
    memory allocation from 4.7GB to 1.3GB when embedding a 3MB literal
    string in source code. See #14741 for profiling results.
    -}

    -- ----------------------------------------------------------------------------
    -- Printing section headers.
    --
    -- If -split-section was specified, include the suffix label, otherwise just
    -- print the section type. For Darwin, where subsections-for-symbols are
    -- used instead, only print section type.
    --
    -- For string literals, additional flags are specified to enable merging of
    -- identical strings in the linker. With -split-sections each string also gets
    -- a unique section to allow strings from unused code to be GC'd.

    pprSectionHeader :: IsLine doc => NCGConfig -> Section -> doc
    pprSectionHeader :: forall doc. IsLine doc => NCGConfig -> Section -> doc
    pprSectionHeader NCGConfig
    config (Section SectionType
    t CLabel
    suffix) =
     case Platform -> OS
    platformOS (NCGConfig -> Platform
    ncgPlatform NCGConfig
    config) of
       OS
    OSAIX     -> SectionType -> doc
    forall doc. IsLine doc => SectionType -> doc
    pprXcoffSectionHeader SectionType
    t
       OS
    OSDarwin  -> SectionType -> doc
    forall doc. IsLine doc => SectionType -> doc
    pprDarwinSectionHeader SectionType
    t
       OS
    _         -> NCGConfig -> SectionType -> CLabel -> doc
    forall doc. IsLine doc => NCGConfig -> SectionType -> CLabel -> doc
    pprGNUSectionHeader NCGConfig
    config SectionType
    t CLabel
    suffix
    {-# SPECIALIZE pprSectionHeader :: NCGConfig -> Section -> SDoc #-}
    {-# SPECIALIZE pprSectionHeader :: NCGConfig -> Section -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    pprGNUSectionHeader :: IsLine doc => NCGConfig -> SectionType -> CLabel -> doc
    pprGNUSectionHeader :: forall doc. IsLine doc => NCGConfig -> SectionType -> CLabel -> doc
    pprGNUSectionHeader NCGConfig
    config SectionType
    t CLabel
    suffix =
      [doc] -> doc
    forall doc. IsLine doc => [doc] -> doc
    hcat [String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".section ", doc
    header, doc
    subsection, doc
    flags]
      where
        sep :: doc
    sep
          | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform = Char -> doc
    forall doc. IsLine doc => Char -> doc
    char Char
    '$'
          | Bool
    otherwise                        = Char -> doc
    forall doc. IsLine doc => Char -> doc
    char Char
    '.'
        platform :: Platform
    platform      = NCGConfig -> Platform
    ncgPlatform NCGConfig
    config
        splitSections :: Bool
    splitSections = NCGConfig -> Bool
    ncgSplitSections NCGConfig
    config
        subsection :: doc
    subsection
          | Bool
    splitSections = doc
    sep doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> Platform -> CLabel -> doc
    forall doc. IsLine doc => Platform -> CLabel -> doc
    pprAsmLabel Platform
    platform CLabel
    suffix
          | Bool
    otherwise     = doc
    forall doc. IsOutput doc => doc
    empty
        header :: doc
    header = case SectionType
    t of
          SectionType
    Text -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".text"
          SectionType
    Data -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".data"
          SectionType
    ReadOnlyData  | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                                    -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".rdata"
                        | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".rodata"
          SectionType
    RelocatableReadOnlyData | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                                    -- Concept does not exist on Windows,
                                    -- So map these to R/O data.
                                              -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".rdata$rel.ro"
                                  | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".data.rel.ro"
          SectionType
    UninitialisedData -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".bss"
          SectionType
    InitArray
            | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                        -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".ctors"
            | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".init_array"
          SectionType
    FiniArray
            | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                        -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".dtors"
            | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".fini_array"
          SectionType
    CString
            | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                        -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".rdata"
            | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".rodata.str"
          OtherSection String
    _ ->
            String -> doc
    forall a. HasCallStack => String -> a
    panic String
    "PprBase.pprGNUSectionHeader: unknown section type"
        flags :: doc
    flags = case SectionType
    t of
          SectionType
    Text
            | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform, Bool
    splitSections
                        -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ",\"xr\""
            | Bool
    splitSections
                        -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ",\"ax\"," doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> Platform -> String -> doc
    forall doc. IsLine doc => Platform -> String -> doc
    sectionType Platform
    platform String
    "progbits"
          SectionType
    CString
            | OS
    OSMinGW32 <- Platform -> OS
    platformOS Platform
    platform
                        -> doc
    forall doc. IsOutput doc => doc
    empty
            | Bool
    otherwise -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ",\"aMS\"," doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> Platform -> String -> doc
    forall doc. IsLine doc => Platform -> String -> doc
    sectionType Platform
    platform String
    "progbits" doc -> doc -> doc
    forall doc. IsLine doc => doc -> doc -> doc
    <> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ",1"
          SectionType
    _ -> doc
    forall doc. IsOutput doc => doc
    empty
    {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> SDoc #-}
    {-# SPECIALIZE pprGNUSectionHeader :: NCGConfig -> SectionType -> CLabel -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    -- XCOFF doesn't support relocating label-differences, so we place all
    -- RO sections into .text[PR] sections
    pprXcoffSectionHeader :: IsLine doc => SectionType -> doc
    pprXcoffSectionHeader :: forall doc. IsLine doc => SectionType -> doc
    pprXcoffSectionHeader SectionType
    t = case SectionType
    t of
      SectionType
    Text                    -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .text[PR]"
      SectionType
    Data                    -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .data[RW]"
      SectionType
    ReadOnlyData            -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .text[PR] # ReadOnlyData"
      SectionType
    RelocatableReadOnlyData -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .text[PR] # RelocatableReadOnlyData"
      SectionType
    CString                 -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .text[PR] # CString"
      SectionType
    UninitialisedData       -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".csect .data[BS]"
      SectionType
    _                       -> String -> doc
    forall a. HasCallStack => String -> a
    panic String
    "pprXcoffSectionHeader: unknown section type"
    {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> SDoc #-}
    {-# SPECIALIZE pprXcoffSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

    pprDarwinSectionHeader :: IsLine doc => SectionType -> doc
    pprDarwinSectionHeader :: forall doc. IsLine doc => SectionType -> doc
    pprDarwinSectionHeader SectionType
    t = case SectionType
    t of
      SectionType
    Text                    -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".text"
      SectionType
    Data                    -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".data"
      SectionType
    ReadOnlyData            -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".const"
      SectionType
    RelocatableReadOnlyData -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".const_data"
      SectionType
    UninitialisedData       -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".data"
      SectionType
    InitArray               -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".section\t__DATA,__mod_init_func,mod_init_funcs"
      SectionType
    FiniArray               -> String -> doc
    forall a. HasCallStack => String -> a
    panic String
    "pprDarwinSectionHeader: fini not supported"
      SectionType
    CString                 -> String -> doc
    forall doc. IsLine doc => String -> doc
    text String
    ".section\t__TEXT,__cstring,cstring_literals"
      OtherSection String
    _          -> String -> doc
    forall a. HasCallStack => String -> a
    panic String
    "pprDarwinSectionHeader: unknown section type"
    {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> SDoc #-}
    {-# SPECIALIZE pprDarwinSectionHeader :: SectionType -> HLine #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable