{-# LANGUAGE CPP #-}
{-# 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

#if !MIN_VERSION_base(4,16,0)
word8ToWord# :: Word# -> Word#
word8ToWord# w = w
{-# INLINE word8ToWord# #-}
#endif

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

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

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

       do1 :: Word8 -> doc
       do1 :: Word8 -> doc
do1 Word8
w | Word8
0x09 forall a. Eq a => a -> a -> Bool
== Word8
w = forall doc. IsLine doc => String -> doc
text String
"\\t"
             | Word8
0x0A forall a. Eq a => a -> a -> Bool
== Word8
w = forall doc. IsLine doc => String -> doc
text String
"\\n"
             | Word8
0x22 forall a. Eq a => a -> a -> Bool
== Word8
w = forall doc. IsLine doc => String -> doc
text String
"\\\""
             | Word8
0x5C forall a. Eq a => a -> a -> Bool
== Word8
w = forall doc. IsLine doc => String -> doc
text String
"\\\\"
               -- ASCII printable characters range
             | Word8
w forall a. Ord a => a -> a -> Bool
>= Word8
0x20 Bool -> Bool -> Bool
&& Word8
w forall a. Ord a => a -> a -> Bool
<= Word8
0x7E = forall doc. IsLine doc => Char -> doc
char (Word8 -> Char
chr' Word8
w)
             | Bool
otherwise = 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 forall a. Num a => a -> a -> a
+ (Word8
w forall a. Bits a => a -> Int -> a
`unsafeShiftR` Int
6) forall a. Bits a => a -> a -> a
.&. Word8
0x07)
                 !x1 :: Char
x1 = Word8 -> Char
chr' (Word8
ord0 forall a. Num a => a -> a -> a
+ (Word8
w forall a. Bits a => a -> Int -> a
`unsafeShiftR` Int
3) forall a. Bits a => a -> a -> a
.&. Word8
0x07)
                 !x2 :: Char
x2 = Word8 -> Char
chr' (Word8
ord0 forall a. Num a => a -> a -> a
+ Word8
w 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 = forall doc. IsLine doc => String -> doc
text String
"\t.string " forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => doc -> doc
doubleQuotes (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
   = forall doc. IsLine doc => String -> doc
text String
"\t.incbin "
     forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => String -> doc
pprFilePathString String
path -- proper escape (see #16389)
     forall doc. IsLine doc => doc -> doc -> 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     -> forall doc. IsLine doc => SectionType -> doc
pprXcoffSectionHeader SectionType
t
   OS
OSDarwin  -> forall doc. IsLine doc => SectionType -> doc
pprDarwinSectionHeader SectionType
t
   OS
_         -> 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 =
  forall doc. IsLine doc => [doc] -> doc
hcat [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 = forall doc. IsLine doc => Char -> doc
char Char
'$'
      | Bool
otherwise                        = 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 forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => Platform -> CLabel -> doc
pprAsmLabel Platform
platform CLabel
suffix
      | Bool
otherwise     = forall doc. IsOutput doc => doc
empty
    header :: doc
header = case SectionType
t of
      SectionType
Text -> forall doc. IsLine doc => String -> doc
text String
".text"
      SectionType
Data -> forall doc. IsLine doc => String -> doc
text String
".data"
      SectionType
ReadOnlyData  | OS
OSMinGW32 <- Platform -> OS
platformOS Platform
platform
                                -> forall doc. IsLine doc => String -> doc
text String
".rdata"
                    | Bool
otherwise -> 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.
                                          -> forall doc. IsLine doc => String -> doc
text String
".rdata$rel.ro"
                              | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
".data.rel.ro"
      SectionType
UninitialisedData -> forall doc. IsLine doc => String -> doc
text String
".bss"
      SectionType
InitArray
        | OS
OSMinGW32 <- Platform -> OS
platformOS Platform
platform
                    -> forall doc. IsLine doc => String -> doc
text String
".ctors"
        | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
".init_array"
      SectionType
FiniArray
        | OS
OSMinGW32 <- Platform -> OS
platformOS Platform
platform
                    -> forall doc. IsLine doc => String -> doc
text String
".dtors"
        | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
".fini_array"
      SectionType
CString
        | OS
OSMinGW32 <- Platform -> OS
platformOS Platform
platform
                    -> forall doc. IsLine doc => String -> doc
text String
".rdata"
        | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
".rodata.str"
      OtherSection String
_ ->
        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
                    -> forall doc. IsLine doc => String -> doc
text String
",\"xr\""
        | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
",\"ax\"," forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => Platform -> String -> doc
sectionType Platform
platform String
"progbits"
      SectionType
CString
        | OS
OSMinGW32 <- Platform -> OS
platformOS Platform
platform
                    -> forall doc. IsOutput doc => doc
empty
        | Bool
otherwise -> forall doc. IsLine doc => String -> doc
text String
",\"aMS\"," forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => Platform -> String -> doc
sectionType Platform
platform String
"progbits" forall doc. IsLine doc => doc -> doc -> doc
<> forall doc. IsLine doc => String -> doc
text String
",1"
      SectionType
_ -> 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                    -> forall doc. IsLine doc => String -> doc
text String
".csect .text[PR]"
  SectionType
Data                    -> forall doc. IsLine doc => String -> doc
text String
".csect .data[RW]"
  SectionType
ReadOnlyData            -> forall doc. IsLine doc => String -> doc
text String
".csect .text[PR] # ReadOnlyData"
  SectionType
RelocatableReadOnlyData -> forall doc. IsLine doc => String -> doc
text String
".csect .text[PR] # RelocatableReadOnlyData"
  SectionType
CString                 -> forall doc. IsLine doc => String -> doc
text String
".csect .text[PR] # CString"
  SectionType
UninitialisedData       -> forall doc. IsLine doc => String -> doc
text String
".csect .data[BS]"
  SectionType
_                       -> 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                    -> forall doc. IsLine doc => String -> doc
text String
".text"
  SectionType
Data                    -> forall doc. IsLine doc => String -> doc
text String
".data"
  SectionType
ReadOnlyData            -> forall doc. IsLine doc => String -> doc
text String
".const"
  SectionType
RelocatableReadOnlyData -> forall doc. IsLine doc => String -> doc
text String
".const_data"
  SectionType
UninitialisedData       -> forall doc. IsLine doc => String -> doc
text String
".data"
  SectionType
InitArray               -> forall doc. IsLine doc => String -> doc
text String
".section\t__DATA,__mod_init_func,mod_init_funcs"
  SectionType
FiniArray               -> forall a. HasCallStack => String -> a
panic String
"pprDarwinSectionHeader: fini not supported"
  SectionType
CString                 -> forall doc. IsLine doc => String -> doc
text String
".section\t__TEXT,__cstring,cstring_literals"
  OtherSection String
_          -> 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