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

module X86.Ppr (
        pprNatCmmTop,
        pprBasicBlock,
        pprSectionHeader,
        pprData,
        pprInstr,
        pprSize,
        pprImm,
        pprDataItem,
)

where

#include "HsVersions.h"
#include "nativeGen/NCG.h"

import X86.Regs
import X86.Instr
import X86.Cond
import Instruction
import Size
import Reg
import PprBase


import BasicTypes       (Alignment)
import OldCmm
import CLabel
import Unique           ( pprUnique, Uniquable(..) )
import Platform
import Pretty
import FastString
import qualified Outputable
import Outputable       (panic, PlatformOutputable)

import Data.Word

#if i386_TARGET_ARCH && darwin_TARGET_OS
import Data.Bits
#endif

-- -----------------------------------------------------------------------------
-- Printing this stuff out

pprNatCmmTop :: Platform -> NatCmmTop (Alignment, CmmStatics) Instr -> Doc
pprNatCmmTop platform (CmmData section dats) =
  pprSectionHeader section $$ pprDatas platform dats

 -- special case for split markers:
pprNatCmmTop platform (CmmProc Nothing lbl (ListGraph [])) = pprLabel platform lbl

 -- special case for code without info table:
pprNatCmmTop platform (CmmProc Nothing lbl (ListGraph blocks)) =
  pprSectionHeader Text $$
  pprLabel platform lbl $$ -- blocks guaranteed not null, so label needed
  vcat (map (pprBasicBlock platform) blocks) $$
  pprSizeDecl platform lbl

pprNatCmmTop platform (CmmProc (Just (Statics info_lbl info)) _entry_lbl (ListGraph blocks)) =
  pprSectionHeader Text $$
  (
#if HAVE_SUBSECTIONS_VIA_SYMBOLS
       pprCLabel_asm (mkDeadStripPreventer info_lbl)
           <> char ':' $$
#endif
       vcat (map (pprData platform) info) $$
       pprLabel platform info_lbl
  ) $$
  vcat (map (pprBasicBlock platform) blocks)
     -- above: Even the first block gets a label, because with branch-chain
     -- elimination, it might be the target of a goto.
#if HAVE_SUBSECTIONS_VIA_SYMBOLS
        -- If we are using the .subsections_via_symbols directive
        -- (available on recent versions of Darwin),
        -- we have to make sure that there is some kind of reference
        -- from the entry code to a label on the _top_ of of the info table,
        -- so that the linker will not think it is unreferenced and dead-strip
        -- it. That's why the label is called a DeadStripPreventer (_dsp).
  $$ text "\t.long "
       <+> pprCLabel_asm info_lbl
       <+> char '-'
       <+> pprCLabel_asm (mkDeadStripPreventer info_lbl)
#endif
   $$ pprSizeDecl platform info_lbl

-- | Output the ELF .size directive.
pprSizeDecl :: Platform -> CLabel -> Doc
pprSizeDecl platform lbl
 | osElfTarget (platformOS platform) =
    ptext (sLit "\t.size") <+> pprCLabel_asm lbl
    <> ptext (sLit ", .-") <> pprCLabel_asm lbl
 | otherwise = empty

pprBasicBlock :: Platform -> NatBasicBlock Instr -> Doc
pprBasicBlock platform (BasicBlock blockid instrs) =
  pprLabel platform (mkAsmTempLabel (getUnique blockid)) $$
  vcat (map (pprInstr platform) instrs)


pprDatas :: Platform -> (Alignment, CmmStatics) -> Doc
pprDatas platform (align, (Statics lbl dats))
 = vcat (pprAlign platform align : pprLabel platform lbl : map (pprData platform) dats)
 -- TODO: could remove if align == 1

pprData :: Platform -> CmmStatic -> Doc
pprData _ (CmmString str)          = pprASCII str

pprData platform (CmmUninitialised bytes)
 | platformOS platform == OSDarwin = ptext (sLit ".space ") <> int bytes
 | otherwise                       = ptext (sLit ".skip ")  <> int bytes

pprData _ (CmmStaticLit lit)       = pprDataItem lit

pprGloblDecl :: CLabel -> Doc
pprGloblDecl lbl
  | not (externallyVisibleCLabel lbl) = empty
  | otherwise = ptext (sLit ".globl ") <> pprCLabel_asm lbl

pprTypeAndSizeDecl :: Platform -> CLabel -> Doc
pprTypeAndSizeDecl platform lbl
 | osElfTarget (platformOS platform) && externallyVisibleCLabel lbl
    = ptext (sLit ".type ") <>
      pprCLabel_asm lbl <> ptext (sLit ", @object")
 | otherwise = empty

pprLabel :: Platform -> CLabel -> Doc
pprLabel platform lbl = pprGloblDecl lbl
                     $$ pprTypeAndSizeDecl platform lbl
                     $$ (pprCLabel_asm lbl <> char ':')


pprASCII :: [Word8] -> Doc
pprASCII str
  = vcat (map do1 str) $$ do1 0
    where
       do1 :: Word8 -> Doc
       do1 w = ptext (sLit "\t.byte\t") <> int (fromIntegral w)

pprAlign :: Platform -> Int -> Doc
pprAlign platform bytes
        = ptext (sLit ".align ") <> int alignment
  where
        alignment = if platformOS platform == OSDarwin
                    then log2 bytes
                    else      bytes

        log2 :: Int -> Int  -- cache the common ones
        log2 1 = 0
        log2 2 = 1
        log2 4 = 2
        log2 8 = 3
        log2 n = 1 + log2 (n `quot` 2)

-- -----------------------------------------------------------------------------
-- pprInstr: print an 'Instr'

instance PlatformOutputable Instr where
    pprPlatform platform instr = Outputable.docToSDoc $ pprInstr platform instr


pprReg :: Platform -> Size -> Reg -> Doc
pprReg _ s r
  = case r of
      RegReal    (RealRegSingle i) -> ppr_reg_no s i
      RegReal    (RealRegPair _ _) -> panic "X86.Ppr: no reg pairs on this arch"
      RegVirtual (VirtualRegI  u)  -> text "%vI_" <> asmSDoc (pprUnique u)
      RegVirtual (VirtualRegHi u)  -> text "%vHi_" <> asmSDoc (pprUnique u)
      RegVirtual (VirtualRegF  u)  -> text "%vF_" <> asmSDoc (pprUnique u)
      RegVirtual (VirtualRegD  u)  -> text "%vD_" <> asmSDoc (pprUnique u)
      RegVirtual (VirtualRegSSE  u) -> text "%vSSE_" <> asmSDoc (pprUnique u)
  where
#if i386_TARGET_ARCH
    ppr_reg_no :: Size -> Int -> Doc
    ppr_reg_no II8   = ppr_reg_byte
    ppr_reg_no II16  = ppr_reg_word
    ppr_reg_no _    = ppr_reg_long

    ppr_reg_byte i = ptext
      (case i of {
         0 -> sLit "%al";     1 -> sLit "%bl";
         2 -> sLit "%cl";     3 -> sLit "%dl";
        _  -> sLit "very naughty I386 byte register"
      })

    ppr_reg_word i = ptext
      (case i of {
         0 -> sLit "%ax";     1 -> sLit "%bx";
         2 -> sLit "%cx";     3 -> sLit "%dx";
         4 -> sLit "%si";     5 -> sLit "%di";
         6 -> sLit "%bp";     7 -> sLit "%sp";
        _  -> sLit "very naughty I386 word register"
      })

    ppr_reg_long i = ptext
      (case i of {
         0 -> sLit "%eax";    1 -> sLit "%ebx";
         2 -> sLit "%ecx";    3 -> sLit "%edx";
         4 -> sLit "%esi";    5 -> sLit "%edi";
         6 -> sLit "%ebp";    7 -> sLit "%esp";
         _  -> ppr_reg_float i
      })
#elif x86_64_TARGET_ARCH
    ppr_reg_no :: Size -> Int -> Doc
    ppr_reg_no II8   = ppr_reg_byte
    ppr_reg_no II16  = ppr_reg_word
    ppr_reg_no II32  = ppr_reg_long
    ppr_reg_no _    = ppr_reg_quad

    ppr_reg_byte i = ptext
      (case i of {
         0 -> sLit "%al";     1 -> sLit "%bl";
         2 -> sLit "%cl";     3 -> sLit "%dl";
         4 -> sLit "%sil";    5 -> sLit "%dil"; -- new 8-bit regs!
         6 -> sLit "%bpl";    7 -> sLit "%spl";
         8 -> sLit "%r8b";    9  -> sLit "%r9b";
        10 -> sLit "%r10b";   11 -> sLit "%r11b";
        12 -> sLit "%r12b";   13 -> sLit "%r13b";
        14 -> sLit "%r14b";   15 -> sLit "%r15b";
        _  -> sLit "very naughty x86_64 byte register"
      })

    ppr_reg_word i = ptext
      (case i of {
         0 -> sLit "%ax";     1 -> sLit "%bx";
         2 -> sLit "%cx";     3 -> sLit "%dx";
         4 -> sLit "%si";     5 -> sLit "%di";
         6 -> sLit "%bp";     7 -> sLit "%sp";
         8 -> sLit "%r8w";    9  -> sLit "%r9w";
        10 -> sLit "%r10w";   11 -> sLit "%r11w";
        12 -> sLit "%r12w";   13 -> sLit "%r13w";
        14 -> sLit "%r14w";   15 -> sLit "%r15w";
        _  -> sLit "very naughty x86_64 word register"
      })

    ppr_reg_long i = ptext
      (case i of {
         0 -> sLit "%eax";    1  -> sLit "%ebx";
         2 -> sLit "%ecx";    3  -> sLit "%edx";
         4 -> sLit "%esi";    5  -> sLit "%edi";
         6 -> sLit "%ebp";    7  -> sLit "%esp";
         8 -> sLit "%r8d";    9  -> sLit "%r9d";
        10 -> sLit "%r10d";   11 -> sLit "%r11d";
        12 -> sLit "%r12d";   13 -> sLit "%r13d";
        14 -> sLit "%r14d";   15 -> sLit "%r15d";
        _  -> sLit "very naughty x86_64 register"
      })

    ppr_reg_quad i = ptext
      (case i of {
         0 -> sLit "%rax";      1 -> sLit "%rbx";
         2 -> sLit "%rcx";      3 -> sLit "%rdx";
         4 -> sLit "%rsi";      5 -> sLit "%rdi";
         6 -> sLit "%rbp";      7 -> sLit "%rsp";
         8 -> sLit "%r8";       9 -> sLit "%r9";
        10 -> sLit "%r10";    11 -> sLit "%r11";
        12 -> sLit "%r12";    13 -> sLit "%r13";
        14 -> sLit "%r14";    15 -> sLit "%r15";
        _  -> ppr_reg_float i
      })
#else
     ppr_reg_no _ = panic "X86.Ppr.ppr_reg_no: no match"
#endif

#if defined(i386_TARGET_ARCH) || defined(x86_64_TARGET_ARCH)
ppr_reg_float :: Int -> LitString
ppr_reg_float i = case i of
        16 -> sLit "%fake0";  17 -> sLit "%fake1"
        18 -> sLit "%fake2";  19 -> sLit "%fake3"
        20 -> sLit "%fake4";  21 -> sLit "%fake5"
        24 -> sLit "%xmm0";   25 -> sLit "%xmm1"
        26 -> sLit "%xmm2";   27 -> sLit "%xmm3"
        28 -> sLit "%xmm4";   29 -> sLit "%xmm5"
        30 -> sLit "%xmm6";   31 -> sLit "%xmm7"
        32 -> sLit "%xmm8";   33 -> sLit "%xmm9"
        34 -> sLit "%xmm10";  35 -> sLit "%xmm11"
        36 -> sLit "%xmm12";  37 -> sLit "%xmm13"
        38 -> sLit "%xmm14";  39 -> sLit "%xmm15"
        _  -> sLit "very naughty x86 register"
#endif

pprSize :: Size -> Doc
pprSize x
 = ptext (case x of
                II8   -> sLit "b"
                II16  -> sLit "w"
                II32  -> sLit "l"
                II64  -> sLit "q"
                FF32  -> sLit "ss"      -- "scalar single-precision float" (SSE2)
                FF64  -> sLit "sd"      -- "scalar double-precision float" (SSE2)
                FF80  -> sLit "t"
                )

pprSize_x87 :: Size -> Doc
pprSize_x87 x
  = ptext $ case x of
                FF32  -> sLit "s"
                FF64  -> sLit "l"
                FF80  -> sLit "t"
                _     -> panic "X86.Ppr.pprSize_x87"

pprCond :: Cond -> Doc
pprCond c
 = ptext (case c of {
                GEU     -> sLit "ae";   LU    -> sLit "b";
                EQQ     -> sLit "e";    GTT   -> sLit "g";
                GE      -> sLit "ge";   GU    -> sLit "a";
                LTT     -> sLit "l";    LE    -> sLit "le";
                LEU     -> sLit "be";   NE    -> sLit "ne";
                NEG     -> sLit "s";    POS   -> sLit "ns";
                CARRY   -> sLit "c";   OFLO  -> sLit "o";
                PARITY  -> sLit "p";   NOTPARITY -> sLit "np";
                ALWAYS  -> sLit "mp"})


pprImm :: Imm -> Doc
pprImm (ImmInt i)     = int i
pprImm (ImmInteger i) = integer i
pprImm (ImmCLbl l)    = pprCLabel_asm l
pprImm (ImmIndex l i) = pprCLabel_asm l <> char '+' <> int i
pprImm (ImmLit s)     = s

pprImm (ImmFloat _)  = ptext (sLit "naughty float immediate")
pprImm (ImmDouble _) = ptext (sLit "naughty double immediate")

pprImm (ImmConstantSum a b) = pprImm a <> char '+' <> pprImm b
pprImm (ImmConstantDiff a b) = pprImm a <> char '-'
                            <> lparen <> pprImm b <> rparen



pprAddr :: Platform -> AddrMode -> Doc
pprAddr _ (ImmAddr imm off)
  = let pp_imm = pprImm imm
    in
    if (off == 0) then
        pp_imm
    else if (off < 0) then
        pp_imm <> int off
    else
        pp_imm <> char '+' <> int off

pprAddr platform (AddrBaseIndex base index displacement)
  = let
        pp_disp  = ppr_disp displacement
        pp_off p = pp_disp <> char '(' <> p <> char ')'
        pp_reg r = pprReg platform archWordSize r
    in
    case (base, index) of
      (EABaseNone,  EAIndexNone) -> pp_disp
      (EABaseReg b, EAIndexNone) -> pp_off (pp_reg b)
      (EABaseRip,   EAIndexNone) -> pp_off (ptext (sLit "%rip"))
      (EABaseNone,  EAIndex r i) -> pp_off (comma <> pp_reg r <> comma <> int i)
      (EABaseReg b, EAIndex r i) -> pp_off (pp_reg b <> comma <> pp_reg r
                                       <> comma <> int i)
      _                         -> panic "X86.Ppr.pprAddr: no match"

  where
    ppr_disp (ImmInt 0) = empty
    ppr_disp imm        = pprImm imm


pprSectionHeader :: Section -> Doc
#if  i386_TARGET_ARCH

#    if darwin_TARGET_OS
pprSectionHeader seg
 = case seg of
        Text                    -> ptext (sLit ".text\n\t.align 2")
        Data                    -> ptext (sLit ".data\n\t.align 2")
        ReadOnlyData            -> ptext (sLit ".const\n.align 2")
        RelocatableReadOnlyData -> ptext (sLit ".const_data\n.align 2")
        UninitialisedData       -> ptext (sLit ".data\n\t.align 2")
        ReadOnlyData16          -> ptext (sLit ".const\n.align 4")
        OtherSection _          -> panic "X86.Ppr.pprSectionHeader: unknown section"

#    else
pprSectionHeader seg
 = case seg of
        Text                    -> ptext (sLit ".text\n\t.align 4,0x90")
        Data                    -> ptext (sLit ".data\n\t.align 4")
        ReadOnlyData            -> ptext (sLit ".section .rodata\n\t.align 4")
        RelocatableReadOnlyData -> ptext (sLit ".section .data\n\t.align 4")
        UninitialisedData       -> ptext (sLit ".section .bss\n\t.align 4")
        ReadOnlyData16          -> ptext (sLit ".section .rodata\n\t.align 16")
        OtherSection _          -> panic "X86.Ppr.pprSectionHeader: unknown section"

#    endif

#elif x86_64_TARGET_ARCH
#    if  darwin_TARGET_OS
pprSectionHeader seg
 = case seg of
        Text                    -> ptext (sLit ".text\n.align 3")
        Data                    -> ptext (sLit ".data\n.align 3")
        ReadOnlyData            -> ptext (sLit ".const\n.align 3")
        RelocatableReadOnlyData -> ptext (sLit ".const_data\n.align 3")
        UninitialisedData       -> ptext (sLit ".data\n\t.align 3")
        ReadOnlyData16          -> ptext (sLit ".const\n.align 4")
        OtherSection _          -> panic "PprMach.pprSectionHeader: unknown section"

#    else
pprSectionHeader seg
 = case seg of
        Text                    -> ptext (sLit ".text\n\t.align 8")
        Data                    -> ptext (sLit ".data\n\t.align 8")
        ReadOnlyData            -> ptext (sLit ".section .rodata\n\t.align 8")
        RelocatableReadOnlyData -> ptext (sLit ".section .data\n\t.align 8")
        UninitialisedData       -> ptext (sLit ".section .bss\n\t.align 8")
        ReadOnlyData16          -> ptext (sLit ".section .rodata.cst16\n\t.align 16")
        OtherSection _          -> panic "PprMach.pprSectionHeader: unknown section"

#    endif

#else
pprSectionHeader _              = panic "X86.Ppr.pprSectionHeader: not defined for this architecture"

#endif




pprDataItem :: CmmLit -> Doc
pprDataItem lit
  = vcat (ppr_item (cmmTypeSize $ cmmLitType lit) lit)
    where
        imm = litToImm lit

        -- These seem to be common:
        ppr_item II8   _ = [ptext (sLit "\t.byte\t") <> pprImm imm]
        ppr_item II32  _ = [ptext (sLit "\t.long\t") <> pprImm imm]

        ppr_item FF32  (CmmFloat r _)
           = let bs = floatToBytes (fromRational r)
             in  map (\b -> ptext (sLit "\t.byte\t") <> pprImm (ImmInt b)) bs

        ppr_item FF64 (CmmFloat r _)
           = let bs = doubleToBytes (fromRational r)
             in  map (\b -> ptext (sLit "\t.byte\t") <> pprImm (ImmInt b)) bs

#if i386_TARGET_ARCH || x86_64_TARGET_ARCH
        ppr_item II16  _ = [ptext (sLit "\t.word\t") <> pprImm imm]
#endif
#if i386_TARGET_ARCH && darwin_TARGET_OS
        ppr_item II64 (CmmInt x _)  =
                [ptext (sLit "\t.long\t")
                    <> int (fromIntegral (fromIntegral x :: Word32)),
                 ptext (sLit "\t.long\t")
                    <> int (fromIntegral
                        (fromIntegral (x `shiftR` 32) :: Word32))]
#endif
#if i386_TARGET_ARCH || (darwin_TARGET_OS && x86_64_TARGET_ARCH)
        ppr_item II64  _ = [ptext (sLit "\t.quad\t") <> pprImm imm]
#endif
#if x86_64_TARGET_ARCH && !darwin_TARGET_OS
        -- x86_64: binutils can't handle the R_X86_64_PC64 relocation
        -- type, which means we can't do pc-relative 64-bit addresses.
        -- Fortunately we're assuming the small memory model, in which
        -- all such offsets will fit into 32 bits, so we have to stick
        -- to 32-bit offset fields and modify the RTS appropriately
        --
        -- See Note [x86-64-relative] in includes/rts/storage/InfoTables.h
        --
        ppr_item II64  x
           | isRelativeReloc x =
                [ptext (sLit "\t.long\t") <> pprImm imm,
                 ptext (sLit "\t.long\t0")]
           | otherwise =
                [ptext (sLit "\t.quad\t") <> pprImm imm]
           where
                isRelativeReloc (CmmLabelDiffOff _ _ _) = True
                isRelativeReloc _ = False
#endif

        ppr_item _ _
                = panic "X86.Ppr.ppr_item: no match"



pprInstr :: Platform -> Instr -> Doc

pprInstr _ (COMMENT _) = empty -- nuke 'em
{-
pprInstr _ (COMMENT s) = ptext (sLit "# ") <> ftext s
-}
pprInstr platform (DELTA d)
   = pprInstr platform (COMMENT (mkFastString ("\tdelta = " ++ show d)))

pprInstr _ (NEWBLOCK _)
   = panic "PprMach.pprInstr: NEWBLOCK"

pprInstr _ (LDATA _ _)
   = panic "PprMach.pprInstr: LDATA"

{-
pprInstr _ (SPILL reg slot)
   = hcat [
        ptext (sLit "\tSPILL"),
        char ' ',
        pprUserReg reg,
        comma,
        ptext (sLit "SLOT") <> parens (int slot)]

pprInstr _ (RELOAD slot reg)
   = hcat [
        ptext (sLit "\tRELOAD"),
        char ' ',
        ptext (sLit "SLOT") <> parens (int slot),
        comma,
        pprUserReg reg]
-}

pprInstr platform (MOV size src dst)
  = pprSizeOpOp platform (sLit "mov") size src dst

pprInstr platform (MOVZxL II32 src dst) = pprSizeOpOp platform (sLit "mov") II32 src dst
        -- 32-to-64 bit zero extension on x86_64 is accomplished by a simple
        -- movl.  But we represent it as a MOVZxL instruction, because
        -- the reg alloc would tend to throw away a plain reg-to-reg
        -- move, and we still want it to do that.

pprInstr platform (MOVZxL sizes src dst) = pprSizeOpOpCoerce platform (sLit "movz") sizes II32 src dst
        -- zero-extension only needs to extend to 32 bits: on x86_64,
        -- the remaining zero-extension to 64 bits is automatic, and the 32-bit
        -- instruction is shorter.

pprInstr platform (MOVSxL sizes src dst) = pprSizeOpOpCoerce platform (sLit "movs") sizes archWordSize src dst

-- here we do some patching, since the physical registers are only set late
-- in the code generation.
pprInstr platform (LEA size (OpAddr (AddrBaseIndex (EABaseReg reg1) (EAIndex reg2 1) (ImmInt 0))) dst@(OpReg reg3))
  | reg1 == reg3
  = pprSizeOpOp platform (sLit "add") size (OpReg reg2) dst

pprInstr platform (LEA size (OpAddr (AddrBaseIndex (EABaseReg reg1) (EAIndex reg2 1) (ImmInt 0))) dst@(OpReg reg3))
  | reg2 == reg3
  = pprSizeOpOp platform (sLit "add") size (OpReg reg1) dst

pprInstr platform (LEA size (OpAddr (AddrBaseIndex (EABaseReg reg1) EAIndexNone displ)) dst@(OpReg reg3))
  | reg1 == reg3
  = pprInstr platform (ADD size (OpImm displ) dst)

pprInstr platform (LEA size src dst) = pprSizeOpOp platform (sLit "lea") size src dst

pprInstr platform (ADD size (OpImm (ImmInt (-1))) dst)
  = pprSizeOp platform (sLit "dec") size dst
pprInstr platform (ADD size (OpImm (ImmInt 1)) dst)
  = pprSizeOp platform (sLit "inc") size dst
pprInstr platform (ADD size src dst)
  = pprSizeOpOp platform (sLit "add") size src dst
pprInstr platform (ADC size src dst)
  = pprSizeOpOp platform (sLit "adc") size src dst
pprInstr platform (SUB size src dst) = pprSizeOpOp platform (sLit "sub") size src dst
pprInstr platform (IMUL size op1 op2) = pprSizeOpOp platform (sLit "imul") size op1 op2

{- A hack.  The Intel documentation says that "The two and three
   operand forms [of IMUL] may also be used with unsigned operands
   because the lower half of the product is the same regardless if
   (sic) the operands are signed or unsigned.  The CF and OF flags,
   however, cannot be used to determine if the upper half of the
   result is non-zero."  So there.
-}
pprInstr platform (AND size src dst) = pprSizeOpOp platform (sLit "and") size src dst
pprInstr platform (OR  size src dst) = pprSizeOpOp platform (sLit "or")  size src dst

pprInstr platform (XOR FF32 src dst) = pprOpOp platform (sLit "xorps") FF32 src dst
pprInstr platform (XOR FF64 src dst) = pprOpOp platform (sLit "xorpd") FF64 src dst
pprInstr platform (XOR size src dst) = pprSizeOpOp platform (sLit "xor")  size src dst

pprInstr platform (NOT size op) = pprSizeOp platform (sLit "not") size op
pprInstr platform (NEGI size op) = pprSizeOp platform (sLit "neg") size op

pprInstr platform (SHL size src dst) = pprShift platform (sLit "shl") size src dst
pprInstr platform (SAR size src dst) = pprShift platform (sLit "sar") size src dst
pprInstr platform (SHR size src dst) = pprShift platform (sLit "shr") size src dst

pprInstr platform (BT  size imm src) = pprSizeImmOp platform (sLit "bt") size imm src

pprInstr platform (CMP size src dst)
  | is_float size =  pprSizeOpOp platform (sLit "ucomi") size src dst -- SSE2
  | otherwise     =  pprSizeOpOp platform (sLit "cmp")   size src dst
  where
        -- This predicate is needed here and nowhere else
    is_float FF32       = True
    is_float FF64       = True
    is_float FF80       = True
    is_float _          = False

pprInstr platform (TEST size src dst) = pprSizeOpOp platform (sLit "test")  size src dst
pprInstr platform (PUSH size op) = pprSizeOp platform (sLit "push") size op
pprInstr platform (POP size op) = pprSizeOp platform (sLit "pop") size op

-- both unused (SDM):
-- pprInstr PUSHA = ptext (sLit "\tpushal")
-- pprInstr POPA = ptext (sLit "\tpopal")

pprInstr _ NOP = ptext (sLit "\tnop")
pprInstr _ (CLTD II32) = ptext (sLit "\tcltd")
pprInstr _ (CLTD II64) = ptext (sLit "\tcqto")

pprInstr platform (SETCC cond op) = pprCondInstr (sLit "set") cond (pprOperand platform II8 op)

pprInstr _ (JXX cond blockid)
  = pprCondInstr (sLit "j") cond (pprCLabel_asm lab)
  where lab = mkAsmTempLabel (getUnique blockid)

pprInstr _ (JXX_GBL cond imm) = pprCondInstr (sLit "j") cond (pprImm imm)

pprInstr _ (JMP (OpImm imm)) = (<>) (ptext (sLit "\tjmp ")) (pprImm imm)
pprInstr platform (JMP op)          = (<>) (ptext (sLit "\tjmp *")) (pprOperand platform archWordSize op)
pprInstr platform (JMP_TBL op _ _ _)  = pprInstr platform (JMP op)
pprInstr _ (CALL (Left imm) _)    = (<>) (ptext (sLit "\tcall ")) (pprImm imm)
pprInstr platform (CALL (Right reg) _)   = (<>) (ptext (sLit "\tcall *")) (pprReg platform archWordSize reg)

pprInstr platform (IDIV sz op)   = pprSizeOp platform (sLit "idiv") sz op
pprInstr platform (DIV sz op)    = pprSizeOp platform (sLit "div")  sz op
pprInstr platform (IMUL2 sz op)  = pprSizeOp platform (sLit "imul") sz op

-- x86_64 only
pprInstr platform (MUL size op1 op2) = pprSizeOpOp platform (sLit "mul") size op1 op2

pprInstr platform (FDIV size op1 op2) = pprSizeOpOp platform (sLit "div") size op1 op2

pprInstr platform (CVTSS2SD from to)      = pprRegReg platform (sLit "cvtss2sd") from to
pprInstr platform (CVTSD2SS from to)      = pprRegReg platform (sLit "cvtsd2ss") from to
pprInstr platform (CVTTSS2SIQ sz from to) = pprSizeSizeOpReg platform (sLit "cvttss2si") FF32 sz from to
pprInstr platform (CVTTSD2SIQ sz from to) = pprSizeSizeOpReg platform (sLit "cvttsd2si") FF64 sz from to
pprInstr platform (CVTSI2SS sz from to)   = pprSizeOpReg platform (sLit "cvtsi2ss") sz from to
pprInstr platform (CVTSI2SD sz from to)   = pprSizeOpReg platform (sLit "cvtsi2sd") sz from to

    -- FETCHGOT for PIC on ELF platforms
pprInstr platform (FETCHGOT reg)
   = vcat [ ptext (sLit "\tcall 1f"),
            hcat [ ptext (sLit "1:\tpopl\t"), pprReg platform II32 reg ],
            hcat [ ptext (sLit "\taddl\t$_GLOBAL_OFFSET_TABLE_+(.-1b), "),
                   pprReg platform II32 reg ]
          ]

    -- FETCHPC for PIC on Darwin/x86
    -- get the instruction pointer into a register
    -- (Terminology note: the IP is called Program Counter on PPC,
    --  and it's a good thing to use the same name on both platforms)
pprInstr platform (FETCHPC reg)
   = vcat [ ptext (sLit "\tcall 1f"),
            hcat [ ptext (sLit "1:\tpopl\t"), pprReg platform II32 reg ]
          ]


-- -----------------------------------------------------------------------------
-- i386 floating-point

-- Simulating a flat register set on the x86 FP stack is tricky.
-- you have to free %st(7) before pushing anything on the FP reg stack
-- so as to preclude the possibility of a FP stack overflow exception.
pprInstr platform g@(GMOV src dst)
   | src == dst
   = empty
   | otherwise
   = pprG platform g (hcat [gtab, gpush src 0, gsemi, gpop dst 1])

-- GLD sz addr dst ==> FLDsz addr ; FSTP (dst+1)
pprInstr platform g@(GLD sz addr dst)
 = pprG platform g (hcat [gtab, text "fld", pprSize_x87 sz, gsp,
                          pprAddr platform addr, gsemi, gpop dst 1])

-- GST sz src addr ==> FLD dst ; FSTPsz addr
pprInstr platform g@(GST sz src addr)
 | src == fake0 && sz /= FF80 -- fstt instruction doesn't exist
 = pprG platform g (hcat [gtab,
                          text "fst", pprSize_x87 sz, gsp, pprAddr platform addr])
 | otherwise
 = pprG platform g (hcat [gtab, gpush src 0, gsemi,
                          text "fstp", pprSize_x87 sz, gsp, pprAddr platform addr])

pprInstr platform g@(GLDZ dst)
 = pprG platform g (hcat [gtab, text "fldz ; ", gpop dst 1])
pprInstr platform g@(GLD1 dst)
 = pprG platform g (hcat [gtab, text "fld1 ; ", gpop dst 1])

pprInstr platform (GFTOI src dst)
   = pprInstr platform (GDTOI src dst)

pprInstr platform g@(GDTOI src dst)
   = pprG platform g (vcat [
         hcat [gtab, text "subl $8, %esp ; fnstcw 4(%esp)"],
         hcat [gtab, gpush src 0],
         hcat [gtab, text "movzwl 4(%esp), ", reg,
                     text " ; orl $0xC00, ", reg],
         hcat [gtab, text "movl ", reg, text ", 0(%esp) ; fldcw 0(%esp)"],
         hcat [gtab, text "fistpl 0(%esp)"],
         hcat [gtab, text "fldcw 4(%esp) ; movl 0(%esp), ", reg],
         hcat [gtab, text "addl $8, %esp"]
     ])
   where
     reg = pprReg platform II32 dst

pprInstr platform (GITOF src dst)
   = pprInstr platform (GITOD src dst)

pprInstr platform g@(GITOD src dst)
   = pprG platform g (hcat [gtab, text "pushl ", pprReg platform II32 src,
                            text " ; fildl (%esp) ; ",
                            gpop dst 1, text " ; addl $4,%esp"])

pprInstr platform g@(GDTOF src dst)
  = pprG platform g (vcat [gtab <> gpush src 0,
                           gtab <> text "subl $4,%esp ; fstps (%esp) ; flds (%esp) ; addl $4,%esp ;",
                           gtab <> gpop dst 1])

{- Gruesome swamp follows.  If you're unfortunate enough to have ventured
   this far into the jungle AND you give a Rat's Ass (tm) what's going
   on, here's the deal.  Generate code to do a floating point comparison
   of src1 and src2, of kind cond, and set the Zero flag if true.

   The complications are to do with handling NaNs correctly.  We want the
   property that if either argument is NaN, then the result of the
   comparison is False ... except if we're comparing for inequality,
   in which case the answer is True.

   Here's how the general (non-inequality) case works.  As an
   example, consider generating the an equality test:

     pushl %eax         -- we need to mess with this
     <get src1 to top of FPU stack>
     fcomp <src2 location in FPU stack> and pop pushed src1
                -- Result of comparison is in FPU Status Register bits
                -- C3 C2 and C0
     fstsw %ax  -- Move FPU Status Reg to %ax
     sahf       -- move C3 C2 C0 from %ax to integer flag reg
     -- now the serious magic begins
     setpo %ah     -- %ah = if comparable(neither arg was NaN) then 1 else 0
     sete  %al     -- %al = if arg1 == arg2 then 1 else 0
     andb %ah,%al  -- %al &= %ah
                   -- so %al == 1 iff (comparable && same); else it holds 0
     decb %al      -- %al == 0, ZeroFlag=1  iff (comparable && same);
                      else %al == 0xFF, ZeroFlag=0
     -- the zero flag is now set as we desire.
     popl %eax

   The special case of inequality differs thusly:

     setpe %ah     -- %ah = if incomparable(either arg was NaN) then 1 else 0
     setne %al     -- %al = if arg1 /= arg2 then 1 else 0
     orb %ah,%al   -- %al = if (incomparable || different) then 1 else 0
     decb %al      -- if (incomparable || different) then (%al == 0, ZF=1)
                                                     else (%al == 0xFF, ZF=0)
-}
pprInstr platform g@(GCMP cond src1 src2)
   | case cond of { NE -> True; _ -> False }
   = pprG platform g (vcat [
        hcat [gtab, text "pushl %eax ; ",gpush src1 0],
        hcat [gtab, text "fcomp ", greg src2 1,
                    text "; fstsw %ax ; sahf ;  setpe %ah"],
        hcat [gtab, text "setne %al ;  ",
              text "orb %ah,%al ;  decb %al ;  popl %eax"]
    ])
   | otherwise
   = pprG platform g (vcat [
        hcat [gtab, text "pushl %eax ; ",gpush src1 0],
        hcat [gtab, text "fcomp ", greg src2 1,
                    text "; fstsw %ax ; sahf ;  setpo %ah"],
        hcat [gtab, text "set", pprCond (fix_FP_cond cond), text " %al ;  ",
              text "andb %ah,%al ;  decb %al ;  popl %eax"]
    ])
    where
        {- On the 486, the flags set by FP compare are the unsigned ones!
           (This looks like a HACK to me.  WDP 96/03)
        -}
        fix_FP_cond :: Cond -> Cond
        fix_FP_cond GE   = GEU
        fix_FP_cond GTT  = GU
        fix_FP_cond LTT  = LU
        fix_FP_cond LE   = LEU
        fix_FP_cond EQQ  = EQQ
        fix_FP_cond NE   = NE
        fix_FP_cond _    = panic "X86.Ppr.fix_FP_cond: no match"
        -- there should be no others


pprInstr platform g@(GABS _ src dst)
   = pprG platform g (hcat [gtab, gpush src 0, text " ; fabs ; ", gpop dst 1])

pprInstr platform g@(GNEG _ src dst)
   = pprG platform g (hcat [gtab, gpush src 0, text " ; fchs ; ", gpop dst 1])

pprInstr platform g@(GSQRT sz src dst)
   = pprG platform g (hcat [gtab, gpush src 0, text " ; fsqrt"] $$
                      hcat [gtab, gcoerceto sz, gpop dst 1])

pprInstr platform g@(GSIN sz l1 l2 src dst)
   = pprG platform g (pprTrigOp "fsin" False l1 l2 src dst sz)

pprInstr platform g@(GCOS sz l1 l2 src dst)
   = pprG platform g (pprTrigOp "fcos" False l1 l2 src dst sz)

pprInstr platform g@(GTAN sz l1 l2 src dst)
   = pprG platform g (pprTrigOp "fptan" True l1 l2 src dst sz)

-- In the translations for GADD, GMUL, GSUB and GDIV,
-- the first two cases are mere optimisations.  The otherwise clause
-- generates correct code under all circumstances.

pprInstr platform g@(GADD _ src1 src2 dst)
   | src1 == dst
   = pprG platform g (text "\t#GADD-xxxcase1" $$
                      hcat [gtab, gpush src2 0,
                            text " ; faddp %st(0),", greg src1 1])
   | src2 == dst
   = pprG platform g (text "\t#GADD-xxxcase2" $$
                      hcat [gtab, gpush src1 0,
                            text " ; faddp %st(0),", greg src2 1])
   | otherwise
   = pprG platform g (hcat [gtab, gpush src1 0,
                            text " ; fadd ", greg src2 1, text ",%st(0)",
                            gsemi, gpop dst 1])


pprInstr platform g@(GMUL _ src1 src2 dst)
   | src1 == dst
   = pprG platform g (text "\t#GMUL-xxxcase1" $$
                      hcat [gtab, gpush src2 0,
                            text " ; fmulp %st(0),", greg src1 1])
   | src2 == dst
   = pprG platform g (text "\t#GMUL-xxxcase2" $$
                      hcat [gtab, gpush src1 0,
                            text " ; fmulp %st(0),", greg src2 1])
   | otherwise
   = pprG platform g (hcat [gtab, gpush src1 0,
                            text " ; fmul ", greg src2 1, text ",%st(0)",
                            gsemi, gpop dst 1])


pprInstr platform g@(GSUB _ src1 src2 dst)
   | src1 == dst
   = pprG platform g (text "\t#GSUB-xxxcase1" $$
                      hcat [gtab, gpush src2 0,
                            text " ; fsubrp %st(0),", greg src1 1])
   | src2 == dst
   = pprG platform g (text "\t#GSUB-xxxcase2" $$
                      hcat [gtab, gpush src1 0,
                            text " ; fsubp %st(0),", greg src2 1])
   | otherwise
   = pprG platform g (hcat [gtab, gpush src1 0,
                            text " ; fsub ", greg src2 1, text ",%st(0)",
                            gsemi, gpop dst 1])


pprInstr platform g@(GDIV _ src1 src2 dst)
   | src1 == dst
   = pprG platform g (text "\t#GDIV-xxxcase1" $$
                      hcat [gtab, gpush src2 0,
                            text " ; fdivrp %st(0),", greg src1 1])
   | src2 == dst
   = pprG platform g (text "\t#GDIV-xxxcase2" $$
                      hcat [gtab, gpush src1 0,
                            text " ; fdivp %st(0),", greg src2 1])
   | otherwise
   = pprG platform g (hcat [gtab, gpush src1 0,
                            text " ; fdiv ", greg src2 1, text ",%st(0)",
                            gsemi, gpop dst 1])


pprInstr _ GFREE
   = vcat [ ptext (sLit "\tffree %st(0) ;ffree %st(1) ;ffree %st(2) ;ffree %st(3)"),
            ptext (sLit "\tffree %st(4) ;ffree %st(5)")
          ]

pprInstr _ _
        = panic "X86.Ppr.pprInstr: no match"


pprTrigOp :: String -> Bool -> CLabel -> CLabel -> Reg -> Reg -> Size -> Doc
pprTrigOp op -- fsin, fcos or fptan
          isTan -- we need a couple of extra steps if we're doing tan
          l1 l2 -- internal labels for us to use
          src dst sz
    = -- We'll be needing %eax later on
      hcat [gtab, text "pushl %eax;"] $$
      -- tan is going to use an extra space on the FP stack
      (if isTan then hcat [gtab, text "ffree %st(6)"] else empty) $$
      -- First put the value in %st(0) and try to apply the op to it
      hcat [gpush src 0, text ("; " ++ op)] $$
      -- Now look to see if C2 was set (overflow, |value| >= 2^63)
      hcat [gtab, text "fnstsw %ax"] $$
      hcat [gtab, text "test   $0x400,%eax"] $$
      -- If we were in bounds then jump to the end
      hcat [gtab, text "je     " <> pprCLabel_asm l1] $$
      -- Otherwise we need to shrink the value. Start by
      -- loading pi, doubleing it (by adding it to itself),
      -- and then swapping pi with the value, so the value we
      -- want to apply op to is in %st(0) again
      hcat [gtab, text "ffree %st(7); fldpi"] $$
      hcat [gtab, text "fadd   %st(0),%st"] $$
      hcat [gtab, text "fxch   %st(1)"] $$
      -- Now we have a loop in which we make the value smaller,
      -- see if it's small enough, and loop if not
      (pprCLabel_asm l2 <> char ':') $$
      hcat [gtab, text "fprem1"] $$
      -- My Debian libc uses fstsw here for the tan code, but I can't
      -- see any reason why it should need to be different for tan.
      hcat [gtab, text "fnstsw %ax"] $$
      hcat [gtab, text "test   $0x400,%eax"] $$
      hcat [gtab, text "jne    " <> pprCLabel_asm l2] $$
      hcat [gtab, text "fstp   %st(1)"] $$
      hcat [gtab, text op] $$
      (pprCLabel_asm l1 <> char ':') $$
      -- Pop the 1.0 tan gave us
      (if isTan then hcat [gtab, text "fstp %st(0)"] else empty) $$
      -- Restore %eax
      hcat [gtab, text "popl %eax;"] $$
      -- And finally make the result the right size
      hcat [gtab, gcoerceto sz, gpop dst 1]

--------------------------

-- coerce %st(0) to the specified size
gcoerceto :: Size -> Doc
gcoerceto FF64 = empty
gcoerceto FF32 = empty --text "subl $4,%esp ; fstps (%esp) ; flds (%esp) ; addl $4,%esp ; "
gcoerceto _    = panic "X86.Ppr.gcoerceto: no match"

gpush :: Reg -> RegNo -> Doc
gpush reg offset
   = hcat [text "fld ", greg reg offset]

gpop :: Reg -> RegNo -> Doc
gpop reg offset
   = hcat [text "fstp ", greg reg offset]

greg :: Reg -> RegNo -> Doc
greg reg offset = text "%st(" <> int (gregno reg - firstfake+offset) <> char ')'

gsemi :: Doc
gsemi = text " ; "

gtab :: Doc
gtab  = char '\t'

gsp :: Doc
gsp   = char ' '

gregno :: Reg -> RegNo
gregno (RegReal (RealRegSingle i)) = i
gregno _           = --pprPanic "gregno" (ppr other)
                     999   -- bogus; only needed for debug printing

pprG :: Platform -> Instr -> Doc -> Doc
pprG platform fake actual
   = (char '#' <> pprGInstr platform fake) $$ actual


pprGInstr :: Platform -> Instr -> Doc
pprGInstr platform (GMOV src dst)   = pprSizeRegReg platform (sLit "gmov") FF64 src dst
pprGInstr platform (GLD sz src dst) = pprSizeAddrReg platform (sLit "gld") sz src dst
pprGInstr platform (GST sz src dst) = pprSizeRegAddr platform (sLit "gst") sz src dst

pprGInstr platform (GLDZ dst) = pprSizeReg platform (sLit "gldz") FF64 dst
pprGInstr platform (GLD1 dst) = pprSizeReg platform (sLit "gld1") FF64 dst

pprGInstr platform (GFTOI src dst) = pprSizeSizeRegReg platform (sLit "gftoi") FF32 II32  src dst
pprGInstr platform (GDTOI src dst) = pprSizeSizeRegReg platform (sLit "gdtoi") FF64 II32 src dst

pprGInstr platform (GITOF src dst) = pprSizeSizeRegReg platform (sLit "gitof") II32 FF32  src dst
pprGInstr platform (GITOD src dst) = pprSizeSizeRegReg platform (sLit "gitod") II32 FF64 src dst
pprGInstr platform (GDTOF src dst) = pprSizeSizeRegReg platform (sLit "gdtof") FF64 FF32 src dst

pprGInstr platform (GCMP co src dst) = pprCondRegReg platform (sLit "gcmp_") FF64 co src dst
pprGInstr platform (GABS sz src dst) = pprSizeRegReg platform (sLit "gabs") sz src dst
pprGInstr platform (GNEG sz src dst) = pprSizeRegReg platform (sLit "gneg") sz src dst
pprGInstr platform (GSQRT sz src dst) = pprSizeRegReg platform (sLit "gsqrt") sz src dst
pprGInstr platform (GSIN sz _ _ src dst) = pprSizeRegReg platform (sLit "gsin") sz src dst
pprGInstr platform (GCOS sz _ _ src dst) = pprSizeRegReg platform (sLit "gcos") sz src dst
pprGInstr platform (GTAN sz _ _ src dst) = pprSizeRegReg platform (sLit "gtan") sz src dst

pprGInstr platform (GADD sz src1 src2 dst) = pprSizeRegRegReg platform (sLit "gadd") sz src1 src2 dst
pprGInstr platform (GSUB sz src1 src2 dst) = pprSizeRegRegReg platform (sLit "gsub") sz src1 src2 dst
pprGInstr platform (GMUL sz src1 src2 dst) = pprSizeRegRegReg platform (sLit "gmul") sz src1 src2 dst
pprGInstr platform (GDIV sz src1 src2 dst) = pprSizeRegRegReg platform (sLit "gdiv") sz src1 src2 dst

pprGInstr _ _ = panic "X86.Ppr.pprGInstr: no match"

pprDollImm :: Imm -> Doc
pprDollImm i =  ptext (sLit "$") <> pprImm i


pprOperand :: Platform -> Size -> Operand -> Doc
pprOperand platform s (OpReg r)   = pprReg platform s r
pprOperand _        _ (OpImm i)   = pprDollImm i
pprOperand platform _ (OpAddr ea) = pprAddr platform ea


pprMnemonic_  :: LitString -> Doc
pprMnemonic_ name =
   char '\t' <> ptext name <> space


pprMnemonic  :: LitString -> Size -> Doc
pprMnemonic name size =
   char '\t' <> ptext name <> pprSize size <> space


pprSizeImmOp :: Platform -> LitString -> Size -> Imm -> Operand -> Doc
pprSizeImmOp platform name size imm op1
  = hcat [
        pprMnemonic name size,
        char '$',
        pprImm imm,
        comma,
        pprOperand platform size op1
    ]


pprSizeOp :: Platform -> LitString -> Size -> Operand -> Doc
pprSizeOp platform name size op1
  = hcat [
        pprMnemonic name size,
        pprOperand platform size op1
    ]


pprSizeOpOp :: Platform -> LitString -> Size -> Operand -> Operand -> Doc
pprSizeOpOp platform name size op1 op2
  = hcat [
        pprMnemonic name size,
        pprOperand platform size op1,
        comma,
        pprOperand platform size op2
    ]


pprOpOp :: Platform -> LitString -> Size -> Operand -> Operand -> Doc
pprOpOp platform name size op1 op2
  = hcat [
        pprMnemonic_ name,
        pprOperand platform size op1,
        comma,
        pprOperand platform size op2
    ]


pprSizeReg :: Platform -> LitString -> Size -> Reg -> Doc
pprSizeReg platform name size reg1
  = hcat [
        pprMnemonic name size,
        pprReg platform size reg1
    ]


pprSizeRegReg :: Platform -> LitString -> Size -> Reg -> Reg -> Doc
pprSizeRegReg platform name size reg1 reg2
  = hcat [
        pprMnemonic name size,
        pprReg platform size reg1,
        comma,
        pprReg platform size reg2
    ]


pprRegReg :: Platform -> LitString -> Reg -> Reg -> Doc
pprRegReg platform name reg1 reg2
  = hcat [
        pprMnemonic_ name,
        pprReg platform archWordSize reg1,
        comma,
        pprReg platform archWordSize reg2
    ]


pprSizeOpReg :: Platform -> LitString -> Size -> Operand -> Reg -> Doc
pprSizeOpReg platform name size op1 reg2
  = hcat [
        pprMnemonic name size,
        pprOperand platform size op1,
        comma,
        pprReg platform archWordSize reg2
    ]

pprCondRegReg :: Platform -> LitString -> Size -> Cond -> Reg -> Reg -> Doc
pprCondRegReg platform name size cond reg1 reg2
  = hcat [
        char '\t',
        ptext name,
        pprCond cond,
        space,
        pprReg platform size reg1,
        comma,
        pprReg platform size reg2
    ]

pprSizeSizeRegReg :: Platform -> LitString -> Size -> Size -> Reg -> Reg -> Doc
pprSizeSizeRegReg platform name size1 size2 reg1 reg2
  = hcat [
        char '\t',
        ptext name,
        pprSize size1,
        pprSize size2,
        space,
        pprReg platform size1 reg1,
        comma,
        pprReg platform size2 reg2
    ]

pprSizeSizeOpReg :: Platform -> LitString -> Size -> Size -> Operand -> Reg -> Doc
pprSizeSizeOpReg platform name size1 size2 op1 reg2
  = hcat [
        pprMnemonic name size2,
        pprOperand platform size1 op1,
        comma,
        pprReg platform size2 reg2
    ]

pprSizeRegRegReg :: Platform -> LitString -> Size -> Reg -> Reg -> Reg -> Doc
pprSizeRegRegReg platform name size reg1 reg2 reg3
  = hcat [
        pprMnemonic name size,
        pprReg platform size reg1,
        comma,
        pprReg platform size reg2,
        comma,
        pprReg platform size reg3
    ]


pprSizeAddrReg :: Platform -> LitString -> Size -> AddrMode -> Reg -> Doc
pprSizeAddrReg platform name size op dst
  = hcat [
        pprMnemonic name size,
        pprAddr platform op,
        comma,
        pprReg platform size dst
    ]


pprSizeRegAddr :: Platform -> LitString -> Size -> Reg -> AddrMode -> Doc
pprSizeRegAddr platform name size src op
  = hcat [
        pprMnemonic name size,
        pprReg platform size src,
        comma,
        pprAddr platform op
    ]


pprShift :: Platform -> LitString -> Size -> Operand -> Operand -> Doc
pprShift platform name size src dest
  = hcat [
        pprMnemonic name size,
        pprOperand platform II8 src,  -- src is 8-bit sized
        comma,
        pprOperand platform size dest
    ]


pprSizeOpOpCoerce :: Platform -> LitString -> Size -> Size -> Operand -> Operand -> Doc
pprSizeOpOpCoerce platform name size1 size2 op1 op2
  = hcat [ char '\t', ptext name, pprSize size1, pprSize size2, space,
        pprOperand platform size1 op1,
        comma,
        pprOperand platform size2 op2
    ]


pprCondInstr :: LitString -> Cond -> Doc -> Doc
pprCondInstr name cond arg
  = hcat [ char '\t', ptext name, pprCond cond, space, arg]