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

module PprBase (
        castFloatToWord8Array,
        castDoubleToWord8Array,
        floatToBytes,
        doubleToBytes,
        pprASCII,
        pprSectionHeader
)

where

import GhcPrelude

import AsmUtils
import CLabel
import Cmm
import DynFlags
import FastString
import Outputable
import Platform

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

import Control.Monad.ST

import Data.Word
import Data.Char



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

castFloatToWord8Array :: STUArray s Int Float -> ST s (STUArray s Int Word8)
castFloatToWord8Array :: STUArray s Int Float -> ST s (STUArray s Int Word8)
castFloatToWord8Array = 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

castDoubleToWord8Array :: STUArray s Int Double -> ST s (STUArray s Int Word8)
castDoubleToWord8Array :: STUArray s Int Double -> ST s (STUArray s Int Word8)
castDoubleToWord8Array = 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

-- floatToBytes and doubleToBytes convert to the host's byte
-- order.  Providing that we're not cross-compiling for a
-- target with the opposite endianness, this should work ok
-- on all targets.

-- ToDo: this stuff is very similar to the shenanigans in PprAbs,
-- could they be merged?

floatToBytes :: Float -> [Int]
floatToBytes :: Float -> [Int]
floatToBytes f :: Float
f
   = (forall s. ST s [Int]) -> [Int]
forall a. (forall s. ST s a) -> a
runST (do
        STUArray s Int Float
arr <- (Int, Int) -> ST s (STUArray s Int Float)
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> m (a i e)
newArray_ ((0::Int),3)
        STUArray s Int Float -> Int -> Float -> ST s ()
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> e -> m ()
writeArray STUArray s Int Float
arr 0 Float
f
        STUArray s Int Word8
arr <- STUArray s Int Float -> ST s (STUArray s Int Word8)
forall s. STUArray s Int Float -> ST s (STUArray s Int Word8)
castFloatToWord8Array STUArray s Int Float
arr
        Word8
i0 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 0
        Word8
i1 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 1
        Word8
i2 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 2
        Word8
i3 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 3
        [Int] -> ST s [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return ((Word8 -> Int) -> [Word8] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral [Word8
i0,Word8
i1,Word8
i2,Word8
i3])
     )

doubleToBytes :: Double -> [Int]
doubleToBytes :: Double -> [Int]
doubleToBytes d :: Double
d
   = (forall s. ST s [Int]) -> [Int]
forall a. (forall s. ST s a) -> a
runST (do
        STUArray s Int Double
arr <- (Int, Int) -> ST s (STUArray s Int Double)
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
(i, i) -> m (a i e)
newArray_ ((0::Int),7)
        STUArray s Int Double -> Int -> Double -> ST s ()
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> e -> m ()
writeArray STUArray s Int Double
arr 0 Double
d
        STUArray s Int Word8
arr <- STUArray s Int Double -> ST s (STUArray s Int Word8)
forall s. STUArray s Int Double -> ST s (STUArray s Int Word8)
castDoubleToWord8Array STUArray s Int Double
arr
        Word8
i0 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 0
        Word8
i1 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 1
        Word8
i2 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 2
        Word8
i3 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 3
        Word8
i4 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 4
        Word8
i5 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 5
        Word8
i6 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 6
        Word8
i7 <- STUArray s Int Word8 -> Int -> ST s Word8
forall (a :: * -> * -> *) e (m :: * -> *) i.
(MArray a e m, Ix i) =>
a i e -> i -> m e
readArray STUArray s Int Word8
arr 7
        [Int] -> ST s [Int]
forall (m :: * -> *) a. Monad m => a -> m a
return ((Word8 -> Int) -> [Word8] -> [Int]
forall a b. (a -> b) -> [a] -> [b]
map Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral [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 Utils.

pprASCII :: [Word8] -> SDoc
pprASCII :: [Word8] -> SDoc
pprASCII str :: [Word8]
str
  -- Transform this given literal bytestring to escaped string and construct
  -- the literal SDoc directly.
  -- See Trac #14741
  -- and Note [Pretty print ASCII when AsmCodeGen]
  = String -> SDoc
text (String -> SDoc) -> String -> SDoc
forall a b. (a -> b) -> a -> b
$ (Word8 -> String -> String) -> String -> [Word8] -> String
forall (t :: * -> *) a b.
Foldable t =>
(a -> b -> b) -> b -> t a -> b
foldr (\w :: Word8
w s :: String
s -> (Int -> String
do1 (Int -> String) -> (Word8 -> Int) -> Word8 -> String
forall b c a. (b -> c) -> (a -> b) -> a -> c
. Word8 -> Int
forall a b. (Integral a, Num b) => a -> b
fromIntegral) Word8
w String -> String -> String
forall a. [a] -> [a] -> [a]
++ String
s) "" [Word8]
str
    where
       do1 :: Int -> String
       do1 :: Int -> String
do1 w :: Int
w | Char
'\t' <- Int -> Char
chr Int
w = "\\t"
             | Char
'\n' <- Int -> Char
chr Int
w = "\\n"
             | Char
'"'  <- Int -> Char
chr Int
w = "\\\""
             | Char
'\\' <- Int -> Char
chr Int
w = "\\\\"
             | Char -> Bool
isPrint (Int -> Char
chr Int
w) = [Int -> Char
chr Int
w]
             | Bool
otherwise = '\\' Char -> String -> String
forall a. a -> [a] -> [a]
: Int -> String
octal Int
w

       octal :: Int -> String
       octal :: Int -> String
octal w :: Int
w = [ Int -> Char
chr (Char -> Int
ord '0' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
w Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 64) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` 8)
                 , Int -> Char
chr (Char -> Int
ord '0' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ (Int
w Int -> Int -> Int
forall a. Integral a => a -> a -> a
`div` 8) Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` 8)
                 , Int -> Char
chr (Char -> Int
ord '0' Int -> Int -> Int
forall a. Num a => a -> a -> a
+ Int
w Int -> Int -> Int
forall a. Integral a => a -> a -> a
`mod` 8)
                 ]

{-
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 Trac #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 :: Platform -> Section -> SDoc
pprSectionHeader :: Platform -> Section -> SDoc
pprSectionHeader platform :: Platform
platform (Section t :: SectionType
t suffix :: CLabel
suffix) =
 case Platform -> OS
platformOS Platform
platform of
   OSAIX     -> SectionType -> SDoc
pprXcoffSectionHeader SectionType
t
   OSDarwin  -> SectionType -> SDoc
pprDarwinSectionHeader SectionType
t
   OSMinGW32 -> SDoc -> SectionType -> CLabel -> SDoc
pprGNUSectionHeader (Char -> SDoc
char '$') SectionType
t CLabel
suffix
   _         -> SDoc -> SectionType -> CLabel -> SDoc
pprGNUSectionHeader (Char -> SDoc
char '.') SectionType
t CLabel
suffix

pprGNUSectionHeader :: SDoc -> SectionType -> CLabel -> SDoc
pprGNUSectionHeader :: SDoc -> SectionType -> CLabel -> SDoc
pprGNUSectionHeader sep :: SDoc
sep t :: SectionType
t suffix :: CLabel
suffix = (DynFlags -> SDoc) -> SDoc
sdocWithDynFlags ((DynFlags -> SDoc) -> SDoc) -> (DynFlags -> SDoc) -> SDoc
forall a b. (a -> b) -> a -> b
$ \dflags :: DynFlags
dflags ->
  let splitSections :: Bool
splitSections = GeneralFlag -> DynFlags -> Bool
gopt GeneralFlag
Opt_SplitSections DynFlags
dflags
      subsection :: SDoc
subsection | Bool
splitSections = SDoc
sep SDoc -> SDoc -> SDoc
<> CLabel -> SDoc
forall a. Outputable a => a -> SDoc
ppr CLabel
suffix
                 | Bool
otherwise     = SDoc
empty
  in  String -> SDoc
text ".section " SDoc -> SDoc -> SDoc
<> PtrString -> SDoc
ptext (DynFlags -> PtrString
header DynFlags
dflags) SDoc -> SDoc -> SDoc
<> SDoc
subsection SDoc -> SDoc -> SDoc
<>
      DynFlags -> SDoc
flags DynFlags
dflags
  where
    header :: DynFlags -> PtrString
header dflags :: DynFlags
dflags = case SectionType
t of
      Text -> String -> PtrString
sLit ".text"
      Data -> String -> PtrString
sLit ".data"
      ReadOnlyData  | OS
OSMinGW32 <- Platform -> OS
platformOS (DynFlags -> Platform
targetPlatform DynFlags
dflags)
                                -> String -> PtrString
sLit ".rdata"
                    | Bool
otherwise -> String -> PtrString
sLit ".rodata"
      RelocatableReadOnlyData | OS
OSMinGW32 <- Platform -> OS
platformOS (DynFlags -> Platform
targetPlatform DynFlags
dflags)
                                -- Concept does not exist on Windows,
                                -- So map these to R/O data.
                                          -> String -> PtrString
sLit ".rdata$rel.ro"
                              | Bool
otherwise -> String -> PtrString
sLit ".data.rel.ro"
      UninitialisedData -> String -> PtrString
sLit ".bss"
      ReadOnlyData16 | OS
OSMinGW32 <- Platform -> OS
platformOS (DynFlags -> Platform
targetPlatform DynFlags
dflags)
                                 -> String -> PtrString
sLit ".rdata$cst16"
                     | Bool
otherwise -> String -> PtrString
sLit ".rodata.cst16"
      CString
        | OS
OSMinGW32 <- Platform -> OS
platformOS (DynFlags -> Platform
targetPlatform DynFlags
dflags)
                    -> String -> PtrString
sLit ".rdata"
        | Bool
otherwise -> String -> PtrString
sLit ".rodata.str"
      OtherSection _ ->
        String -> PtrString
forall a. String -> a
panic "PprBase.pprGNUSectionHeader: unknown section type"
    flags :: DynFlags -> SDoc
flags dflags :: DynFlags
dflags = case SectionType
t of
      CString
        | OS
OSMinGW32 <- Platform -> OS
platformOS (DynFlags -> Platform
targetPlatform DynFlags
dflags)
                    -> SDoc
empty
        | Bool
otherwise -> String -> SDoc
text ",\"aMS\"," SDoc -> SDoc -> SDoc
<> String -> SDoc
sectionType "progbits" SDoc -> SDoc -> SDoc
<> String -> SDoc
text ",1"
      _ -> SDoc
empty

-- XCOFF doesn't support relocating label-differences, so we place all
-- RO sections into .text[PR] sections
pprXcoffSectionHeader :: SectionType -> SDoc
pprXcoffSectionHeader :: SectionType -> SDoc
pprXcoffSectionHeader t :: SectionType
t = String -> SDoc
text (String -> SDoc) -> String -> SDoc
forall a b. (a -> b) -> a -> b
$ case SectionType
t of
     Text                    -> ".csect .text[PR]"
     Data                    -> ".csect .data[RW]"
     ReadOnlyData            -> ".csect .text[PR] # ReadOnlyData"
     RelocatableReadOnlyData -> ".csect .text[PR] # RelocatableReadOnlyData"
     ReadOnlyData16          -> ".csect .text[PR] # ReadOnlyData16"
     CString                 -> ".csect .text[PR] # CString"
     UninitialisedData       -> ".csect .data[BS]"
     OtherSection _          ->
       String -> String
forall a. String -> a
panic "PprBase.pprXcoffSectionHeader: unknown section type"

pprDarwinSectionHeader :: SectionType -> SDoc
pprDarwinSectionHeader :: SectionType -> SDoc
pprDarwinSectionHeader t :: SectionType
t =
  PtrString -> SDoc
ptext (PtrString -> SDoc) -> PtrString -> SDoc
forall a b. (a -> b) -> a -> b
$ case SectionType
t of
     Text -> String -> PtrString
sLit ".text"
     Data -> String -> PtrString
sLit ".data"
     ReadOnlyData -> String -> PtrString
sLit ".const"
     RelocatableReadOnlyData -> String -> PtrString
sLit ".const_data"
     UninitialisedData -> String -> PtrString
sLit ".data"
     ReadOnlyData16 -> String -> PtrString
sLit ".const"
     CString -> String -> PtrString
sLit ".section\t__TEXT,__cstring,cstring_literals"
     OtherSection _ ->
       String -> PtrString
forall a. String -> a
panic "PprBase.pprDarwinSectionHeader: unknown section type"