packages feed

ghc-9.14.1: GHC/CmmToAsm/LA64/Ppr.hs

module GHC.CmmToAsm.LA64.Ppr (pprNatCmmDecl, pprInstr) where

import GHC.Prelude hiding (EQ)

import GHC.CmmToAsm.LA64.Regs
import GHC.CmmToAsm.LA64.Instr
import GHC.CmmToAsm.LA64.Cond
import GHC.CmmToAsm.Config
import GHC.CmmToAsm.Format
import GHC.CmmToAsm.Ppr
import GHC.CmmToAsm.Types
import GHC.CmmToAsm.Utils
import GHC.Cmm hiding (topInfoTable)
import GHC.Cmm.BlockId
import GHC.Cmm.CLabel
import GHC.Cmm.Dataflow.Label
import GHC.Platform
import GHC.Platform.Reg
import GHC.Types.Unique ( pprUniqueAlways, getUnique )
import GHC.Utils.Outputable
import GHC.Types.Basic (Alignment, alignmentBytes, mkAlignment)
import GHC.Utils.Panic

pprNatCmmDecl :: forall doc. (IsDoc doc) => NCGConfig -> NatCmmDecl RawCmmStatics Instr -> doc

pprNatCmmDecl config (CmmData section dats) =
  pprSectionAlign config section $$ pprDatas config dats

pprNatCmmDecl config proc@(CmmProc top_info lbl _ (ListGraph blocks)) =
  let platform = ncgPlatform config

      pprProcAlignment :: doc
      pprProcAlignment = maybe empty (pprAlign . mkAlignment) (ncgProcAlignment config)
   in pprProcAlignment
        $$ case topInfoTable proc of
          Nothing ->
            -- special case for code without info table:
            pprSectionAlign config (Section Text lbl)
              $$
              -- do not
              -- pprProcAlignment config $$
              pprLabel platform lbl
              $$ vcat (map (pprBasicBlock config top_info) blocks) -- blocks guaranteed not null, so label needed
              $$ ppWhen
                (ncgDwarfEnabled config)
                (line (pprBlockEndLabel platform lbl) $$ line (pprProcEndLabel platform lbl))
              $$ pprSizeDecl platform lbl
          Just (CmmStaticsRaw info_lbl _) ->
            pprSectionAlign config (Section Text info_lbl)
              $$
              -- pprProcAlignment config $$
              ( if platformHasSubsectionsViaSymbols platform
                  then line (pprAsmLabel platform (mkDeadStripPreventer info_lbl) <> char ':')
                  else empty
              )
              $$ vcat (map (pprBasicBlock config top_info) blocks)
              $$ ppWhen (ncgDwarfEnabled config) (line (pprProcEndLabel platform info_lbl))
              $$
              -- above: Even the first block gets a label, because with branch-chain
              -- elimination, it might be the target of a goto.
              ( if platformHasSubsectionsViaSymbols platform
                  then -- See Note [Subsections Via Symbols]

                    line
                      $ text "\t.long "
                      <+> pprAsmLabel platform info_lbl
                      <+> char '-'
                      <+> pprAsmLabel platform (mkDeadStripPreventer info_lbl)
                  else empty
              )
              $$ pprSizeDecl platform info_lbl
{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> SDoc #-}
{-# SPECIALIZE pprNatCmmDecl :: NCGConfig -> NatCmmDecl RawCmmStatics Instr -> HDoc #-} -- see Note [SPECIALIZE to HDoc] in GHC.Utils.Outputable

pprProcEndLabel :: IsLine doc => Platform -> CLabel -- ^ Procedure
                -> doc
pprProcEndLabel platform lbl =
    pprAsmLabel platform (mkAsmTempProcEndLabel lbl) <> colon

pprBlockEndLabel :: IsLine doc => Platform -> CLabel -- ^ Block name
                 -> doc
pprBlockEndLabel platform lbl =
    pprAsmLabel platform (mkAsmTempEndLabel lbl) <> colon

pprLabel :: IsDoc doc => Platform -> CLabel -> doc
pprLabel platform lbl =
   pprGloblDecl platform lbl
   $$ pprTypeDecl platform lbl
   $$ line (pprAsmLabel platform lbl <> char ':')

pprAlign :: (IsDoc doc) => Alignment -> doc
pprAlign alignment =
  -- .balign is stable, whereas .align is platform dependent.
  line $ text "\t.balign " <> int (alignmentBytes alignment)

-- | Print appropriate alignment for the given section type.
--
-- Currently, this always aligns to a full machine word (8 byte.) A future
-- improvement could be to really do this per section type (though, it's
-- probably not a big gain.)
pprAlignForSection :: (IsDoc doc) => SectionType -> doc
pprAlignForSection _seg = pprAlign . mkAlignment $ 8

-- Print section header and appropriate alignment for that section.
-- This will e.g. emit a header like:
--
--     .section .text
--     .balign 8
--
pprSectionAlign :: IsDoc doc => NCGConfig -> Section -> doc
pprSectionAlign _config (Section (OtherSection _) _) =
  panic "LA64.Ppr.pprSectionAlign: unknown section"
pprSectionAlign config sec@(Section seg _) =
    line (pprSectionHeader config sec)
    $$ pprAlignForSection seg

-- | Output the ELF .size directive
pprSizeDecl :: (IsDoc doc) => Platform -> CLabel -> doc
pprSizeDecl platform lbl
  | osElfTarget (platformOS platform) =
      line $ text "\t.size" <+> pprAsmLabel platform lbl <> text ", .-" <> pprAsmLabel platform lbl
pprSizeDecl _ _ = empty

pprBasicBlock ::
  (IsDoc doc) =>
  NCGConfig ->
  LabelMap RawCmmStatics ->
  NatBasicBlock Instr ->
  doc

pprBasicBlock config info_env (BasicBlock blockid instrs)
  = maybe_infotable $
    pprLabel platform asmLbl $$
    vcat (map (pprInstr platform) (id {-detectTrivialDeadlock-} optInstrs)) $$
    ppWhen (ncgDwarfEnabled config) (
      -- Emit both end labels since this may end up being a standalone
      -- top-level block
      line (pprBlockEndLabel platform asmLbl
         <> pprProcEndLabel platform asmLbl)
    )
  where
    -- Filter out identity moves. E.g. mov x18, x18 will be dropped.
    optInstrs = filter f instrs
      where f (MOV o1 o2) | o1 == o2 = False
            f _ = True

    asmLbl = blockLbl blockid
    platform = ncgPlatform config
    maybe_infotable c = case mapLookup blockid info_env of
       Nothing   -> c
       Just (CmmStaticsRaw info_lbl info) ->
          --  pprAlignForSection platform Text $$
           infoTableLoc $$
           vcat (map (pprData config) info) $$
           pprLabel platform info_lbl $$
           c $$
           ppWhen (ncgDwarfEnabled config)
              (line (pprBlockEndLabel platform info_lbl))
    -- Make sure the info table has the right .loc for the block
    -- coming right after it. See Note [Info Offset]
    infoTableLoc = case instrs of
      (l@LOCATION{} : _) -> pprInstr platform l
      _other             -> empty

pprDatas :: IsDoc doc => NCGConfig -> RawCmmStatics -> doc
-- See Note [emit-time elimination of static indirections] in "GHC.Cmm.CLabel".
pprDatas config (CmmStaticsRaw alias [CmmStaticLit (CmmLabel lbl), CmmStaticLit ind, _, _])
  | lbl == mkIndStaticInfoLabel
  , let labelInd (CmmLabelOff l _) = Just l
        labelInd (CmmLabel l) = Just l
        labelInd _ = Nothing
  , Just ind' <- labelInd ind
  , alias `mayRedirectTo` ind'
  = pprGloblDecl (ncgPlatform config) alias
    $$ line (text ".equiv" <+> pprAsmLabel (ncgPlatform config) alias <> comma <> pprAsmLabel (ncgPlatform config) ind')

pprDatas config (CmmStaticsRaw lbl dats)
  = vcat (pprLabel platform lbl : map (pprData config) dats)
   where
      platform = ncgPlatform config

pprData :: IsDoc doc => NCGConfig -> CmmStatic -> doc
pprData _config (CmmString str) = line (pprString str)
pprData _config (CmmFileEmbed path _) = line (pprFileEmbed path)

pprData config (CmmUninitialised bytes)
 = line $ let platform = ncgPlatform config
          in if platformOS platform == OSDarwin
                then text ".space " <> int bytes
                else text ".skip "  <> int bytes

pprData config (CmmStaticLit lit) = pprDataItem config lit

pprGloblDecl :: IsDoc doc => Platform -> CLabel -> doc
pprGloblDecl platform lbl
  | not (externallyVisibleCLabel lbl) = empty
  | otherwise = line (text "\t.globl " <> pprAsmLabel platform lbl)

-- Always use objects for info tables
--
-- See discussion in X86.Ppr for why this is necessary.  Essentially we need to
-- ensure that we never pass function symbols when we might want to lookup the
-- info table.  If we did, we could end up with procedure linking tables
-- (PLT)s, and thus the lookup wouldn't point to the function, but into the
-- jump table.
--
-- Fun fact: The LLVMMangler exists to patch this issue su on the LLVM side as
-- well.
pprLabelType' :: IsLine doc => Platform -> CLabel -> doc
pprLabelType' platform lbl =
  if isCFunctionLabel lbl || functionOkInfoTable
    then text "@function"
    else text "@object"
  where
    functionOkInfoTable = platformTablesNextToCode platform &&
      isInfoTableLabel lbl && not (isCmmInfoTableLabel lbl) && not (isConInfoTableLabel lbl)

-- this is called pprTypeAndSizeDecl in PPC.Ppr
pprTypeDecl :: IsDoc doc => Platform -> CLabel -> doc
pprTypeDecl platform lbl
    = if osElfTarget (platformOS platform) && externallyVisibleCLabel lbl
      then line (text ".type " <> pprAsmLabel platform lbl <> text ", " <> pprLabelType' platform lbl)
      else empty

pprDataItem :: IsDoc doc => NCGConfig -> CmmLit -> doc
pprDataItem config lit
  = lines_ (ppr_item (cmmTypeFormat $ cmmLitType platform lit) lit)
    where
        platform = ncgPlatform config

        imm = litToImm lit

        ppr_item II8  _ = [text "\t.byte\t"  <> pprDataImm platform imm]
        ppr_item II16 _ = [text "\t.short\t" <> pprDataImm platform imm]
        ppr_item II32 _ = [text "\t.long\t"  <> pprDataImm platform imm]
        ppr_item II64 _ = [text "\t.quad\t"  <> pprDataImm platform imm]

        ppr_item FF32  (CmmFloat r _)
           = let bs = floatToBytes (fromRational r)
             in  map (\b -> text "\t.byte\t" <> int (fromIntegral b)) bs

        ppr_item FF64 (CmmFloat r _)
           = let bs = doubleToBytes (fromRational r)
             in  map (\b -> text "\t.byte\t" <> int (fromIntegral b)) bs

        ppr_item _ _ = pprPanic "pprDataItem:ppr_item" (text $ show lit)

-- | Pretty print an immediate value in the @data@ section
-- This does not include any checks. We rely on the Assembler to check for
-- errors. Use `pprOpImm` for immediates in instructions (operands.)
pprDataImm :: IsLine doc => Platform -> Imm -> doc
pprDataImm _ (ImmInt i)     = int i
pprDataImm _ (ImmInteger i) = integer i
pprDataImm p (ImmCLbl l)    = pprAsmLabel p l
pprDataImm p (ImmIndex l i) = pprAsmLabel p l <> char '+' <> int i
pprDataImm _ (ImmLit s)     = ftext s
pprDataImm _ (ImmFloat f) = float (fromRational f)
pprDataImm _ (ImmDouble d) = double (fromRational d)

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

asmComment :: SDoc -> SDoc
asmComment c = text "#" <+> c

asmDoubleslashComment :: SDoc -> SDoc
asmDoubleslashComment c = text "//" <+> c

asmMultilineComment :: SDoc -> SDoc
asmMultilineComment c =  text "/*" $+$ c $+$ text "*/"

-- | Pretty print an immediate operand of an instruction
pprOpImm :: (IsLine doc) => Platform -> Imm -> doc
pprOpImm platform imm = case imm of
  ImmInt i -> int i
  ImmInteger i -> integer i
  ImmCLbl l -> char '=' <> pprAsmLabel platform l
  ImmFloat f -> float (fromRational f)
  ImmDouble d -> double (fromRational d)
  _ -> pprPanic "LA64.Ppr.pprOpImm" (text "Unsupported immediate for instruction operands:" <+> (text . show) imm)

negOp :: Operand -> Operand
negOp (OpImm (ImmInt i)) = OpImm (ImmInt (negate i))
negOp (OpImm (ImmInteger i)) = OpImm (ImmInteger (negate i))
negOp op = pprPanic "LA64.negOp" (text $ show op)

pprOp :: IsLine doc => Platform -> Operand -> doc
pprOp plat op = case op of
  OpReg w r                 -> pprReg w r
  OpImm imm                 -> pprOpImm plat imm
  OpAddr (AddrRegReg r1 r2) -> pprReg W64 r1 <> comma <+> pprReg W64 r2
  OpAddr (AddrRegImm r imm) -> pprReg W64 r <> comma <+> pprOpImm plat imm
  OpAddr (AddrReg r)        -> pprReg W64 r <+> text ", 0"

pprReg :: forall doc. IsLine doc => Width -> Reg -> doc
pprReg w r = case r of
  RegReal    (RealRegSingle i) -> ppr_reg_no i
  -- virtual regs should not show up, but this is helpful for debugging.
  RegVirtual (VirtualRegI u)   -> text "%vI_" <> pprUniqueAlways u
  -- RegVirtual (VirtualRegF u)   -> text "%vF_" <> pprUniqueAlways u
  RegVirtual (VirtualRegD u)   -> text "%vD_" <> pprUniqueAlways u
  _                            -> pprPanic "LA64.pprReg" (text (show r) <+> ppr w)

  where
    ppr_reg_no :: Int -> doc
    -- LoongArch's registers must be started from `$[fr]`
    -- General Purpose Registers
    ppr_reg_no 0  = text "$zero"
    ppr_reg_no 1  = text "$ra"
    ppr_reg_no 2  = text "$tp"
    ppr_reg_no 3  = text "$sp"
    ppr_reg_no 4  = text "$a0"
    ppr_reg_no 5  = text "$a1"
    ppr_reg_no 6  = text "$a2"
    ppr_reg_no 7  = text "$a3"
    ppr_reg_no 8  = text "$a4"
    ppr_reg_no 9  = text "$a5"
    ppr_reg_no 10 = text "$a6"
    ppr_reg_no 11 = text "$a7"
    ppr_reg_no 12 = text "$t0"
    ppr_reg_no 13 = text "$t1"
    ppr_reg_no 14 = text "$t2"
    ppr_reg_no 15 = text "$t3"
    ppr_reg_no 16 = text "$t4"
    ppr_reg_no 17 = text "$t5"
    ppr_reg_no 18 = text "$t6"
    ppr_reg_no 19 = text "$t7"
    ppr_reg_no 20 = text "$t8"
    ppr_reg_no 21 = text "$u0"  -- Reserverd
    ppr_reg_no 22 = text "$fp"
    ppr_reg_no 23 = text "$s0"
    ppr_reg_no 24 = text "$s1"
    ppr_reg_no 25 = text "$s2"
    ppr_reg_no 26 = text "$s3"
    ppr_reg_no 27 = text "$s4"
    ppr_reg_no 28 = text "$s5"
    ppr_reg_no 29 = text "$s6"
    ppr_reg_no 30 = text "$s7"
    ppr_reg_no 31 = text "$s8"

    -- Floating Point Registers
    ppr_reg_no 32 = text "$fa0"
    ppr_reg_no 33 = text "$fa1"
    ppr_reg_no 34 = text "$fa2"
    ppr_reg_no 35 = text "$fa3"
    ppr_reg_no 36 = text "$fa4"
    ppr_reg_no 37 = text "$fa5"
    ppr_reg_no 38 = text "$fa6"
    ppr_reg_no 39 = text "$fa7"
    ppr_reg_no 40 = text "$ft0"
    ppr_reg_no 41 = text "$ft1"
    ppr_reg_no 42 = text "$ft2"
    ppr_reg_no 43 = text "$ft3"
    ppr_reg_no 44 = text "$ft4"
    ppr_reg_no 45 = text "$ft5"
    ppr_reg_no 46 = text "$ft6"
    ppr_reg_no 47 = text "$ft7"
    ppr_reg_no 48 = text "$ft8"
    ppr_reg_no 49 = text "$ft9"
    ppr_reg_no 50 = text "$ft10"
    ppr_reg_no 51 = text "$ft11"
    ppr_reg_no 52 = text "$ft12"
    ppr_reg_no 53 = text "$ft13"
    ppr_reg_no 54 = text "$ft14"
    ppr_reg_no 55 = text "$ft15"
    ppr_reg_no 56 = text "$fs0"
    ppr_reg_no 57 = text "$fs1"
    ppr_reg_no 58 = text "$fs2"
    ppr_reg_no 59 = text "$fs3"
    ppr_reg_no 60 = text "$fs4"
    ppr_reg_no 61 = text "$fs5"
    ppr_reg_no 62 = text "$fs6"
    ppr_reg_no 63 = text "$fs7"

    ppr_reg_no i
         | i < 0 = pprPanic "Unexpected register number (min is 0)" (ppr w <+> int i)
         | i > 63 = pprPanic "Unexpected register number (max is 63)" (ppr w <+> int i)
         -- no support for widths > W64.
         | otherwise = pprPanic "Unsupported width in register (max is 64)" (ppr w <+> int i)

-- | Single precission `Operand` (floating-point)
isSingleOp :: Operand -> Bool
isSingleOp (OpReg W32 _) = True
isSingleOp _ = False

-- | Double precission `Operand` (floating-point)
isDoubleOp :: Operand -> Bool
isDoubleOp (OpReg W64 _) = True
isDoubleOp _ = False

-- | `Operand` is an immediate value
isImmOp :: Operand -> Bool
isImmOp (OpImm _) = True
isImmOp _ = False

-- | `Operand` is an immediate @0@ value
isImmZero :: Operand -> Bool
isImmZero (OpImm (ImmFloat 0)) = True
isImmZero (OpImm (ImmDouble 0)) = True
isImmZero (OpImm (ImmInt 0)) = True
isImmZero _ = False

pprInstr :: IsDoc doc => Platform -> Instr -> doc
pprInstr platform instr = case instr of
  -- Meta Instructions ---------------------------------------------------------
  -- see Note [dualLine and dualDoc] in GHC.Utils.Outputable
  COMMENT s  -> dualDoc (asmComment s) empty
  MULTILINE_COMMENT s -> dualDoc (asmMultilineComment s) empty
  ANN d i -> dualDoc (pprInstr platform i <+> asmDoubleslashComment d) (pprInstr platform i)

  LOCATION file line' col _name
    -> line (text "\t.loc" <+> int file <+> int line' <+> int col)
  DELTA d   -> dualDoc (asmComment $ text "\tdelta = " <> int d) empty
  NEWBLOCK _ -> panic "PprInstr: NEWBLOCK"
  LDATA _ _ -> panic "PprInstr: NEWBLOCK"

  -- Pseudo Instructions -------------------------------------------------------

  PUSH_STACK_FRAME -> lines_ [ text "\taddi.d $sp, $sp, -16"
                             , text "\tst.d   $ra, $sp, 8"
                             , text "\tst.d   $fp, $sp, 0"
                             , text "\taddi.d $fp, $sp, 16"
                             ]

  POP_STACK_FRAME -> lines_  [ text "\tld.d   $fp, $sp, 0"
                             , text "\tld.d   $ra, $sp, 8"
                             , text "\taddi.d $sp, $sp, 16"
                             ]


  -- ===========================================================================
  -- LoongArch64 Instruction Set
  -- Basic Integer Instructions ------------------------------------------------
  -- 1. Arithmetic Instructions ------------------------------------------------
    -- ADD.{W/D}, SUB.{W/D}
    -- ADDI.{W/D}, ADDU16I.D
  ADD  o1 o2 o3
    | isFloatOp o2 && isFloatOp o3 && isSingleOp o2 && isSingleOp o3 -> op3 (text "\tfadd.s") o1 o2 o3
    | isFloatOp o2 && isFloatOp o3 && isDoubleOp o2 && isDoubleOp o3 -> op3 (text "\tfadd.d") o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tadd.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tadd.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 -> op3 (text "\taddi.w") o1 o2 o3
    | OpReg W64 _ <- o2, isImmOp o3 -> op3 (text "\taddi.d") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: ADD error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
    -- TODO: Not complete.
    -- Here we should add addu16i.d for optimizations of accelerating GOT accession
    -- with ldptr.w/d, stptr.w/d
  SUB  o1 o2 o3
    | isFloatOp o2 && isFloatOp o3 && isSingleOp o2 && isSingleOp o3 -> op3 (text "\tfsub.s") o1 o2 o3
    | isFloatOp o2 && isFloatOp o3 && isDoubleOp o2 && isDoubleOp o3 -> op3 (text "\tfsub.d") o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tsub.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tsub.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 -> op3 (text "\taddi.w") o1 o2 (negOp o3)
    | OpReg W64 _ <- o2, isImmOp o3 -> op3 (text "\taddi.d") o1 o2 (negOp o3)
    | otherwise -> pprPanic "LA64.ppr: SUB error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
    -- ALSL.{W[U]/D}
  ALSL  o1 o2 o3 o4
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3, isImmOp o4 -> op4 (text "\talsl.w") o1 o2 o3 o4
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3, isImmOp o4 -> op4 (text "\talsl.d") o1 o2 o3 o4
    | otherwise -> pprPanic "LA64.ppr: ALSL error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  ALSLU  o1 o2 o3 o4 -> op4 (text "\talsl.wu") o1 o2 o3 o4
    -- LoongArch-Assembler should implement following pesudo instructions, here we can directly use them.
    -- li.w rd, s32
    -- li.w rd, u32
    -- li.d rd, s64
    -- li.d rd, u64
    --
    -- # Load with one instruction
    -- ori dst, $zero, imm[11:0]
    --
    -- # Load with two instructions
    -- lu12i.w dst, imm[31:12]
    -- ori     dst, dst, imm[11:0]
    --
    -- # Load with four instructions
    -- lu12i.w dst, imm[31:12]
    -- ori     dst, dst, imm[11:0]
    -- lu32i.d dst, imm[51:32]
    -- lu52i.d dst, dst, imm[63:52]

  --  -- LU12I.W, LU32I.D, LU52I.D
  LU12I  o1 o2 -> op2 (text "\tlu12i.w") o1 o2
  LU32I  o1 o2 -> op2 (text "\tlu32i.d") o1 o2
  LU52I  o1 o2 o3 -> op3 (text "\tlu52i.d") o1 o2 o3
    -- SSLT[U]
    -- SSLT[U]I
  SSLT  o1 o2 o3
    | isImmOp o3 -> op3 (text "\tslti") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3  -> op3 (text "\tslt") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: SSLT error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  SSLTU  o1 o2 o3
    | isImmOp o3 -> op3 (text "\tsltui") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tsltu") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: SSLTU error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
    -- PCADDI, PCADDU121, PCADDU18l, PCALAU12I
  PCADDI  o1 o2     -> op2 (text "\tpcaddi") o1 o2
  PCADDU12I  o1 o2  -> op2 (text "\tpcaddu12i") o1 o2
  PCADDU18I  o1 (OpImm (ImmCLbl lbl))  ->
    lines_ [
      text "\tpcaddu18i" <+> pprOp platform o1 <> comma <+> text "%call36(" <+> pprAsmLabel platform lbl <+> text ")"
           ]
  PCALAU12I  o1 o2  -> op2 (text "\tpcalau12i") o1 o2
    -- AND, OR, NOR, XOR, ANDN, ORN
    -- ANDI, ORI, XORI: zero-extention
  AND  o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tand") o1 o2 o3
    | OpReg W64 _ <- o2, isImmOp o3 -> op3 (text "\tandi") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: AND error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  OR  o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tor") o1 o2 o3
    | OpReg W64 _ <- o2, isImmOp o3 -> op3 (text "\tori") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: OR error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  XOR  o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\txor") o1 o2 o3
    | OpReg W64 _ <- o2, isImmOp o3 -> op3 (text "\txori") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: XOR error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  NOR  o1 o2 o3   -> op3 (text "\tnor") o1 o2 o3
  ANDN  o1 o2 o3  -> op3 (text "\tandn") o1 o2 o3
  ORN  o1 o2 o3   -> op3 (text "\torn") o1 o2 o3

  -----------------------------------------------------------------------------
  -- Pseudo instructions
  -- NOP, alias for "andi r0, r0, r0"
  NOP -> line $ text "\tnop"
  -- NEG o1 o2, alias for "sub o1, r0, o2"
  NEG o1 o2
    | isFloatOp o2 && isSingleOp o2 -> op2 (text "\tfneg.s") o1 o2
    | isFloatOp o2 && isDoubleOp o2 -> op2 (text "\tfneg.d") o1 o2
    | OpReg W32 _ <- o2 -> op3 (text "\tsub.w" ) o1 zero o2
    | OpReg W64 _ <- o2 -> op3 (text "\tsub.d" ) o1 zero o2
    | otherwise -> pprPanic "LA64.ppr: NEG error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)
  -- Here we can do more simplitcations.
  -- To be honest, floating point instructions are too scarce, so maybe
  -- we should reimplement some pseudo instructions with others.
  MOV o1 o2
    | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 -> op2 (text "\tfmov.s") o1 o2
    | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 -> op2 (text "\tfmov.d") o1 o2
    | isFloatOp o1 && isImmZero o2 && isSingleOp o1 -> op2 (text "\tmovgr2fr.w") o1 zero
    | isFloatOp o1 && isImmZero o2 && isDoubleOp o1 -> op2 (text "\tmovgr2fr.d") o1 zero
    | isFloatOp o1 && not (isFloatOp o2) && isSingleOp o1 -> op2 (text "\tmovgr2fr.w") o1 o2
    | isFloatOp o1 && not (isFloatOp o2) && isDoubleOp o1 -> op2 (text "\tmovgr2fr.d") o1 o2
    | not (isFloatOp o1) && isFloatOp o2 && isSingleOp o2 -> op2 (text "\tmovfr2gr.s") o1 o2
    | not (isFloatOp o1) && isFloatOp o2 && isDoubleOp o2 -> op2 (text "\tmovfr2gr.d") o1 o2
    | isImmOp o2, (OpImm (ImmInt i)) <- o2, fitsInNbits 12 (fromIntegral i) ->
      lines_ [text "\taddi.d" <+> pprOp platform o1 <> comma <+> pprOp platform x0 <+> comma <> pprOp platform o2]
    | isImmOp o2, (OpImm (ImmInteger i)) <- o2, fitsInNbits 12 (fromIntegral i) ->
      lines_ [text "\taddi.d" <+> pprOp platform o1 <> comma <+> pprOp platform x0 <+> comma <> pprOp platform o2]
    | OpReg W64 _ <- o2 -> op2 (text "\tmove") o1 o2
    | OpReg _ _ <- o2  ->
      lines_ [
        text "\tbstrpick.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform (OpImm (ImmInt ((widthToInt (min (widthFromOpReg o1) (widthFromOpReg o2))) - 1))) <+> text ", 0"
             ]
    -- TODO: Maybe we can do more.
    -- Let the assembler do these concret things.
    | isImmOp o2 ->
      lines_ [text "\tli.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2]
    | otherwise -> pprPanic "LA64.ppr: MOV error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)

  -- CSET pesudo instrcutions implementation
  CSET cond dst o1 o2 -> case cond of
    -- SEQ dst, rd, rs -> [SUB rd, rd, rs;  sltui dst, rd, 1]
    EQ | isIntOp o1 && isIntOp o2 ->
      lines_ [
              subFor o1 o2,
              text "\tsltui" <+> pprOp platform dst <> comma <+> pprOp platform dst <> comma <+> pprOp platform (OpImm (ImmInt 1))
             ]
    EQ | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.seq.d $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    EQ | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.seq.s $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    -- SNE rd, rs -> [SUB rd, rd, rs;   sltu rd, zero, rs]
    NE | isIntOp o1 && isIntOp o2 ->
      lines_ [
              subFor o1 o2,
              text "\tsltu" <+> pprOp platform dst <> comma <+> text "$r0" <+> comma <+> pprOp platform dst
             ]
    NE | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.cune.d $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    NE | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.cune.s $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    SLT -> lines_ [ sltFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform o2 ]
    -- SLE rd, rs -> [SLT rd, rs;  xori rd, rs, o1]
    SLE ->
      lines_ [
              sltFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\txori" <+>  pprOp platform dst <> comma <+> pprOp platform dst <> comma <+> pprOp platform (OpImm (ImmInt 1))
             ]
    -- SGE rd, rs -> [SLT rd, rs;  xori rd, rs, o1]
    SGE ->
      lines_ [
              sltFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\txori" <+>  pprOp platform dst <> comma <+> pprOp platform dst <> comma <+> pprOp platform (OpImm (ImmInt 1))
             ]
    -- SGT rd, rs -> [SLT rd, rs]
    SGT -> lines_ [ sltFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o1 ]

    ULT -> lines_ [ sltuFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform o2 ]
    ULE ->
      lines_ [
              sltuFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\txori" <+> pprOp platform dst <> comma <+> pprOp platform dst <> comma <+> pprOp platform (OpImm (ImmInt 1))
             ]
    -- UGE rd, rs -> [SLTU rd, rs;  xori rd, rs, 1]
    UGE ->
      lines_ [
              sltuFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\txori" <+>  pprOp platform dst <> comma <+> pprOp platform dst <> comma <+> pprOp platform (OpImm (ImmInt 1))
             ]
    -- SGTU rd, rs -> [SLTU rd, rs]
    UGT -> lines_ [ sltuFor o1 o2 <+> pprOp platform dst <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o1 ]

    -- TODO:
    -- LoongArch's floating point instrcutions don't write the compared result to an interger register, instead of cc.
    -- Fcond dst o1 o2 -> [fcmp.cond.[s/d] fcc0 o1 o2;  movcf2gr dst, fcc0]
    FLT | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.slt.d $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FLE | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.sle.d $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FGT | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.slt.d $fcc0," <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FGE | isFloatOp o1 && isFloatOp o2 && isDoubleOp o1 && isDoubleOp o2 ->
      lines_ [
              text "\tfcmp.sle.d $fcc0," <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]

    FLT | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.slt.s $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FLE | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.sle.s $fcc0," <+> pprOp platform o1 <> comma <+> pprOp platform o2,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FGT | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.slt.s $fcc0," <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]
    FGE | isFloatOp o1 && isFloatOp o2 && isSingleOp o1 && isSingleOp o2 ->
      lines_ [
              text "\tfcmp.sle.s $fcc0," <+> pprOp platform o2 <> comma <+> pprOp platform o1,
              text "\tmovcf2gr" <+> pprOp platform dst <+> text ", $fcc0"
             ]

    _ -> pprPanic "LA64.ppr: CSET error: " (pprCond cond <+> pprOp platform dst <> comma <+> (ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)

    where
      subFor o1 o2  | (OpReg W64 _) <- dst, (OpImm _) <- o2  =
                        text "\taddi.d" <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform (negOp o2)
                    | (OpReg W64 _) <- dst, (OpReg W64 _) <- o2 =
                        text "\tsub.d" <+> pprOp platform dst <> comma <+> pprOp platform o1 <> comma <+> pprOp platform o2
                    | otherwise = pprPanic "LA64.ppr: unknown subFor format: " ((ppr (widthFromOpReg dst)) <+> pprOp platform dst <+> (ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)

      sltFor o1 o2  | (OpReg W64 _) <- dst, (OpImm _) <- o2   = text "\tslti"
                    | (OpReg W64 _) <- dst, (OpReg W64 _) <- o2 = text "\tslt"
                    | otherwise = pprPanic "LA64.ppr: unknown sltFor format: " ((ppr (widthFromOpReg dst)) <+> pprOp platform dst <+> (ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)

      sltuFor o1 o2 | (OpReg W64 _) <- dst, (OpImm _) <- o2   = text "\tsltui"
                    | (OpReg W64 _) <- dst, (OpReg W64 _) <- o2 = text "\tsltu"
                    | otherwise = pprPanic "LA64.ppr: unknown sltuFor format: " ((ppr (widthFromOpReg dst)) <+> pprOp platform dst <+> (ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)

    -- MUL.{W/D}, MULH, {W[U]/D[U]}, 'h' means high 32bit.
    -- MULW.D.W[U]
  MUL  o1 o2 o3
    | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 && isSingleOp o1 && isSingleOp o2 && isSingleOp o3 -> op3 (text "\tfmul.s") o1 o2 o3
    | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 && isDoubleOp o1 && isDoubleOp o2 && isDoubleOp o3 -> op3 (text "\tfmul.d") o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmul.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tmul.d") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MUL error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MULW   o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmulw.d.w") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MULW error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MULWU  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmulw.d.wu") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MULWU error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MULH  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmulh.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o2 -> op3 (text "\tmulh.d") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MULH error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MULHU  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmulh.wu") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tmulh.du") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MULHU error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
    -- DIV.{W[U]/D[U]}, MOD.{W[U]/D[U]}
  DIV  o1 o2 o3
    | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 && isSingleOp o1 && isSingleOp o2 && isSingleOp o3 -> op3 (text "\tfdiv.s") o1 o2 o3
    | isFloatOp o1 && isFloatOp o2 && isFloatOp o3 && isDoubleOp o1 && isDoubleOp o2 && isDoubleOp o3 -> op3 (text "\tfdiv.d") o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tdiv.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tdiv.d") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: DIV error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  DIVU  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tdiv.wu") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tdiv.du") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: DIVU error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MOD  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmod.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tmod.d") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MOD error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  MODU  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tmod.wu") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tmod.du") o1 o2 o3
    | otherwise -> pprPanic "LA64.ppr: MODU error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  -- 2. Bit-shift Instuctions --------------------------------------------------
    -- SLL.W, SRL.W, SRA.W, ROTR.W
    -- SLL.D, SRL.D, SRA.D, ROTR.D
    -- SLLI.W, SRLI.W, SRAI.W, ROTRI.W
    -- SLLI.D, SRLI.D, SRAI.D, ROTRI.D
  SLL  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tsll.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tsll.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 ->
        lines_ [text "\tslli.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | OpReg W64 _ <- o2, isImmOp o3 ->
        lines_ [text "\tslli.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | otherwise -> pprPanic "LA64.ppr: SLL error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  SRL  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tsrl.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tsrl.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 ->
        lines_ [text "\tsrli.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | OpReg W64 _ <- o2, isImmOp o3 ->
        lines_ [text "\tsrli.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | otherwise -> pprPanic "LA64.ppr: SRL error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  SRA  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\tsra.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\tsra.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 ->
        lines_ [text "\tsrai.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | OpReg W64 _ <- o2, isImmOp o3 ->
        lines_ [text "\tsrai.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | otherwise -> pprPanic "LA64.ppr: SRA error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  ROTR  o1 o2 o3
    | OpReg W32 _ <- o2, OpReg W32 _ <- o3 -> op3 (text "\trotr.w") o1 o2 o3
    | OpReg W64 _ <- o2, OpReg W64 _ <- o3 -> op3 (text "\trotr.d") o1 o2 o3
    | OpReg W32 _ <- o2, isImmOp o3 ->
        lines_ [text "\trotri.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | OpReg W64 _ <- o2, isImmOp o3 ->
        lines_ [text "\trotri.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3]
    | otherwise -> pprPanic "LA64.ppr: ROTR error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2 <+> (ppr (widthFromOpReg o3)) <+> pprOp platform o3)
  -- 3. Bit-manupulation Instructions ------------------------------------------
    -- EXT.W{B/H}
  EXT o1 o2
    | OpReg W8 _ <- o2  -> op2 (text "\text.w.b") o1 o2
    | OpReg W16 _ <- o2 -> op2 (text "\text.w.h") o1 o2
    | otherwise -> pprPanic "LA64.ppr: EXT error: " ((ppr (widthFromOpReg o1)) <+> pprOp platform o1 <+> (ppr (widthFromOpReg o2)) <+> pprOp platform o2)
    -- CL{O/Z}.{W/D}, CT{O/Z}.{W/D}
  CLO o1 o2
    | OpReg W32 _ <- o2 -> op2 (text "\tclo.w") o1 o2
    | OpReg W64 _ <- o2 -> op2 (text "\tclo.d") o1 o2
    | otherwise -> pprPanic "LA64.ppr: CLO error" (pprOp platform o1 <+> pprOp platform o2)
  CLZ o1 o2
    | OpReg W32 _ <- o2 -> op2 (text "\tclz.w") o1 o2
    | OpReg W64 _ <- o2 -> op2 (text "\tclz.d") o1 o2
    | otherwise -> pprPanic "LA64.ppr: CLZ error" (pprOp platform o1 <+> pprOp platform o2)
  CTO o1 o2
    | OpReg W32 _ <- o2 -> op2 (text "\tcto.w") o1 o2
    | OpReg W64 _ <- o2 -> op2 (text "\tcto.d") o1 o2
    | otherwise -> pprPanic "LA64.ppr: CTO error" (pprOp platform o1 <+> pprOp platform o2)
  CTZ o1 o2
    | OpReg W32 _ <- o2 -> op2 (text "\tctz.w") o1 o2
    | OpReg W64 _ <- o2 -> op2 (text "\tctz.d") o1 o2
    | otherwise -> pprPanic "LA64.ppr: CTZ error" (pprOp platform o1 <+> pprOp platform o2)
    -- BYTEPICK.{W/D} rd, rj, rk, sa2/sa3
  BYTEPICK o1 o2 o3 o4
    | OpReg W32 _ <- o2 -> op4 (text "\tbytepick.w") o1 o2 o3 o4
    | OpReg W64 _ <- o2 -> op4 (text "\tbytepick.d") o1 o2 o3 o4
    | otherwise -> pprPanic "LA64.ppr: BYTEPICK error" (pprOp platform o1 <+> pprOp platform o2 <+> pprOp platform o3 <+> pprOp platform o4)
    -- REVB.{2H/4H/2W/D}
  REVB2H o1 o2 -> op2 (text "\trevb.2h") o1 o2
  REVB4H o1 o2 -> op2 (text "\trevb.4h") o1 o2
  REVB2W o1 o2 -> op2 (text "\trevb.2w") o1 o2
  REVBD  o1 o2 -> op2 (text "\trevb.d") o1 o2
    -- REVH.{2W/D}
  REVH2W o1 o2 -> op2 (text "\trevh.2w") o1 o2
  REVHD o1 o2 -> op2 (text "\trevh.d") o1 o2
    -- BITREV.{4B/8B}
    -- BITREV.{W/D}
  BITREV4B o1 o2 -> op2 (text "\tbitrev.4b") o1 o2
  BITREV8B o1 o2 -> op2 (text "\tbitrev.8b") o1 o2
  BITREVW o1 o2 -> op2 (text "\tbitrev.w") o1 o2
  BITREVD o1 o2 -> op2 (text "\tbitrev.d") o1 o2
    -- BSTRINS.{W/D}
  BSTRINS II64 o1 o2 o3 o4 -> op4 (text "\tbstrins.d") o1 o2 o3 o4
  BSTRINS II32 o1 o2 o3 o4 -> op4 (text "\tbstrins.w") o1 o2 o3 o4
    -- BSTRPICK.{W/D}
  BSTRPICK II64 o1 o2 o3 o4 -> op4 (text "\tbstrpick.d") o1 o2 o3 o4
  BSTRPICK II32 o1 o2 o3 o4 -> op4 (text "\tbstrpick.w") o1 o2 o3 o4
    -- MASKEQZ rd, rj, rk:  if rk == 0 ? rd = 0 : rd = rj
  MASKEQZ o1 o2 o3 -> op3 (text "\tmaskeqz") o1 o2 o3
    -- MASKNEZ:  if rk == 0 ? rd = 0 : rd = rj
  MASKNEZ o1 o2 o3 -> op3 (text "\tmasknez") o1 o2 o3
  -- 4. Branch Instructions ----------------------------------------------------
    -- BEQ, BNE, BLT[U], BGE[U]   rj, rd, off16
    -- BEQZ, BNEZ   rj, off21
    -- B
    -- BL
    -- JIRL
    -- jr rd = jirl $zero, rd, 0: Commonly used for subroutine return.
  J (TReg r) -> line $ text "\tjirl" <+> text "$r0" <> comma <+> pprReg W64 r <> comma <+> text " 0"
  J_TBL _ _ r    -> pprInstr platform (B (TReg r))

  B (TBlock bid) -> line $ text "\tb" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  B (TLabel lbl) -> line $ text "\tb" <+> pprAsmLabel platform lbl
  B (TReg r)     -> line $ text "\tjr" <+> pprReg W64 r

  BL (TBlock bid) _ -> line $ text "\tbl" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  BL (TLabel lbl) _ -> line $ text "\tbl" <+> pprAsmLabel platform lbl
  BL (TReg r) _    -> line $ text "\tjirl" <+> text "$r1" <> comma <+> pprReg W64 r <> comma <+> text " 0"

  CALL (TBlock bid) _ -> line $ text "\tcall36" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  CALL (TLabel lbl) _ -> line $ text "\tcall36" <+> pprAsmLabel platform lbl
  CALL (TReg r) _ -> line $ text "\tjirl" <+> text "$r1" <> comma <+> pprReg W64 r <> comma <+> text " 0"

  CALL36 (TBlock bid) -> line $ text "\tcall36" <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  CALL36 (TLabel lbl) -> line $ text "\tcall36" <+> pprAsmLabel platform lbl
  CALL36 _ -> panic "LA64.ppr: CALL36: Not to registers!"
  TAIL36 r (TBlock bid) -> line $ text "\ttail36" <+> pprOp platform r <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  TAIL36 r (TLabel lbl) -> line $ text "\ttail36" <+> pprOp platform r <> comma <+> pprAsmLabel platform lbl
  TAIL36 _ _ -> panic "LA64.ppr: TAIL36: Not to registers!"

  BCOND1 c j d (TBlock bid) -> case c of
    SLE ->
      line $ text "\tbge" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
    SGT ->
      line $ text "\tblt" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
    ULE ->
      line $ text "\tbgeu" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
    UGT ->
      line $ text "\tbltu" <+> pprOp platform d <> comma <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
    _ -> line $ text "\t" <> pprBcond c <+> pprOp platform j <> comma <+> pprOp platform d <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))

  BCOND1 _ _ _ (TLabel _) -> panic "LA64.ppr: BCOND1: No conditional branching to TLabel!"

  BCOND1 _ _ _ (TReg _) -> panic "LA64.ppr: BCOND1: No conditional branching to registers!"

  -- Reuse t8(IP) register
  BCOND c j d (TBlock bid) -> case c of
    SLE ->
      lines_ [
              text "\tslt $t8, " <+> pprOp platform  d <> comma <+> pprOp platform j,
              text "\tbeqz $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    SGT ->
      lines_ [
              text "\tslt $t8, " <+> pprOp platform  d <> comma <+> pprOp platform j,
              text "\tbnez $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    ULE ->
      lines_ [
              text "\tsltu $t8, " <+> pprOp platform  d <> comma <+> pprOp platform j,
              text "\tbeqz $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    UGT ->
      lines_ [
              text "\tsltu $t8, " <+> pprOp platform  d <> comma <+> pprOp platform j,
              text "\tbnez $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    EQ ->
      lines_ [
              text "\tsub.d $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbeqz $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    NE ->
      lines_ [
              text "\tsub.d $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbnez $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    SLT ->
      lines_ [
              text "\tslt $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbnez $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    SGE ->
      lines_ [
              text "\tslt $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbeqz $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    ULT ->
      lines_ [
              text "\tsltu $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbnez $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    UGE ->
      lines_ [
              text "\tsltu $t8, " <+> pprOp platform  j <> comma <+> pprOp platform d,
              text "\tbeqz $t8, " <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
             ]
    _ -> panic "LA64.ppr: BCOND: Unsupported cond!"

  BCOND _ _ _ (TLabel _) -> panic "LA64.ppr: BCOND: No conditional branching to TLabel!"

  BCOND _ _ _ (TReg _) -> panic "LA64.ppr: BCOND: No conditional branching to registers!"

  BEQZ j (TBlock bid) ->
    line $ text "\tbeqz" <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  BEQZ j (TLabel lbl) ->
    line $ text "\tbeqz" <+> pprOp platform j <> comma <+> pprAsmLabel platform lbl
  BEQZ _ (TReg _)     -> panic "LA64.ppr: BEQZ: No conditional branching to registers!"

  BNEZ j (TBlock bid) ->
    line $ text "\tbnez" <+> pprOp platform j <> comma <+> pprAsmLabel platform (mkLocalBlockLabel (getUnique bid))
  BNEZ j (TLabel lbl) ->
    line $ text "\tbnez" <+> pprOp platform j <> comma <+> pprAsmLabel platform lbl
  BNEZ _ (TReg _)     -> panic "LA64.ppr: BNEZ: No conditional branching to registers!"

  -- 5. Common Memory Access Instructions --------------------------------------
    -- LD.{B[U]/H[U]/W[U]/D}, ST.{B/H/W/D}: AddrRegImm
    -- LD: load, ST: store, x: offset in register, u: load unsigned imm.
    -- LD format dst src: 'src' means final address, not single register or immdiate.
  -- Load symbol's address
  LD _fmt o1 (OpImm (ImmIndex lbl' off)) | Just (_, lbl) <- dynamicLinkerLabelInfo lbl' ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%got_pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\tld.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%got_pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
            , text "\taddi.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> int off
           ]
  LD _fmt o1 (OpImm (ImmIndex lbl off)) | isForeignLabel lbl ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%got_pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\tld.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%got_pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
            , text "\taddi.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> int off
           ]
  LD _fmt o1 (OpImm (ImmIndex lbl off)) ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\taddi.d"    <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
            , text "\taddi.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> int off
           ]

  LD _fmt o1 (OpImm (ImmCLbl lbl')) | Just (_, lbl) <- dynamicLinkerLabelInfo lbl' ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%got_pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\tld.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%got_pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
           ]
  LD _fmt o1 (OpImm (ImmCLbl lbl)) | isForeignLabel lbl ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%got_pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\tld.d"   <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%got_pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
           ]
  LD _fmt o1 (OpImm (ImmCLbl lbl)) ->
    lines_ [ text "\tpcalau12i" <+> pprOp platform o1 <> comma <+> text "%pc_hi20(" <> pprAsmLabel platform lbl <> text ")"
            , text "\taddi.d"    <+> pprOp platform o1 <> comma <+> pprOp platform o1 <> comma <+> text "%pc_lo12(" <> pprAsmLabel platform lbl <> text ")"
           ]

  LD II8  o1 o2 -> op2 (text "\tld.b") o1 o2
  LD II16 o1 o2 -> op2 (text "\tld.h") o1 o2
  LD II32 o1 o2 -> op2 (text "\tld.w") o1 o2
  LD II64 o1 o2 -> op2 (text "\tld.d") o1 o2
  LD FF32 o1 o2 -> op2 (text "\tfld.s") o1 o2
  LD FF64 o1 o2 -> op2 (text "\tfld.d") o1 o2

  LDU II8  o1 o2 -> op2 (text "\tld.bu") o1 o2
  LDU II16 o1 o2 -> op2 (text "\tld.hu") o1 o2
  LDU II32 o1 o2 -> op2 (text "\tld.wu") o1 o2
  LDU II64 o1 o2 -> op2 (text "\tld.d") o1 o2   -- double words (64bit) cannot be sign extended by definition
  LDU FF32 o1 o2@(OpAddr (AddrReg _))       -> op2 (text "\tfld.s") o1 o2
  LDU FF32 o1 o2@(OpAddr (AddrRegImm _ _))  -> op2 (text "\tfld.s") o1 o2
  LDU FF64 o1 o2@(OpAddr (AddrReg _))       -> op2 (text "\tfld.d") o1 o2
  LDU FF64 o1 o2@(OpAddr (AddrRegImm _ _))  -> op2 (text "\tfld.d") o1 o2
  LDU f o1 o2 -> pprPanic "Unsupported unsigned load" ((text.show) f <+> pprOp platform o1 <+> pprOp platform o2)

  ST II8  o1 o2 -> op2 (text "\tst.b") o1 o2
  ST II16 o1 o2 -> op2 (text "\tst.h") o1 o2
  ST II32 o1 o2 -> op2 (text "\tst.w") o1 o2
  ST II64 o1 o2 -> op2 (text "\tst.d") o1 o2
  ST FF32 o1 o2 -> op2 (text "\tfst.s") o1 o2
  ST FF64 o1 o2 -> op2 (text "\tfst.d") o1 o2

    -- LDPTR.{W/D}, STPTR.{W/D}: AddrRegImm: AddrRegImm
  LDPTR II32 o1 o2 -> op2 (text "\tldptr.w") o1 o2
  LDPTR II64 o1 o2 -> op2 (text "\tldptr.d") o1 o2
  STPTR II32 o1 o2 -> op2 (text "\tstptr.w") o1 o2
  STPTR II64 o1 o2 -> op2 (text "\tstptr.d") o1 o2

    -- LDX.{B[U]/H[U]/W[U]/D}, STX.{B/H/W/D}: AddrRegReg
  LDX II8   o1 o2 -> op2 (text "\tldx.b")  o1 o2
  LDX II16  o1 o2 -> op2 (text "\tldx.h")  o1 o2
  LDX II32  o1 o2 -> op2 (text "\tldx.w")  o1 o2
  LDX II64  o1 o2 -> op2 (text "\tldx.d")  o1 o2
  LDX FF32  o1 o2 -> op2 (text "\tfldx.s") o1 o2
  LDX FF64  o1 o2 -> op2 (text "\tfldx.d") o1 o2
  LDXU II8  o1 o2 -> op2 (text "\tldx.bu") o1 o2
  LDXU II16 o1 o2 -> op2 (text "\tldx.hu") o1 o2
  LDXU II32 o1 o2 -> op2 (text "\tldx.wu") o1 o2
  LDXU II64 o1 o2 -> op2 (text "\tldx.d")  o1 o2
  STX II8   o1 o2 -> op2 (text "\tstx.b")  o1 o2
  STX II16  o1 o2 -> op2 (text "\tstx.h")  o1 o2
  STX II32  o1 o2 -> op2 (text "\tstx.w")  o1 o2
  STX II64  o1 o2 -> op2 (text "\tstx.d")  o1 o2
  STX FF32  o1 o2 -> op2 (text "\tfstx.s") o1 o2
  STX FF64  o1 o2 -> op2 (text "\tfstx.d") o1 o2

  PRELD h o1@(OpAddr (AddrRegImm _ _)) -> op2 (text "\tpreld") h o1
  -- 6. Bound Check Memory Access Instructions ---------------------------------
    -- LD{GT/LE}.{B/H/W/D}, ST{GT/LE}.{B/H/W/D}
  -- 7. Atomic Memory Access Instructions --------------------------------------
    -- AM{SWAP/ADD/AND/OR/XOR/MAX/MIN}[DB].{W/D}, AM{MAX/MIN}[_DB].{WU/DU}
    -- AM.{SWAP/ADD}[_DB].{B/H}
    -- AMCAS[_DB].{B/H/W/D}
    -- LL.{W/D}, SC.{W/D}
    -- SC.Q
    -- LL.ACQ.{W/D}, SC.REL.{W/D}
  -- 8. Barrier Instructions ---------------------------------------------------
    -- DBAR, IBAR
  DBAR h -> line $ text "\tdbar" <+> pprBarrierType h
  IBAR h -> line $ text "\tibar" <+> pprBarrierType h

    -- Floating-point convert precision
  FCVT o1@(OpReg W32 _) o2@(OpReg W64 _) -> op2 (text "\tfcvt.s.d") o1 o2
  FCVT o1@(OpReg W64 _) o2@(OpReg W32 _) -> op2 (text "\tfcvt.d.s") o1 o2
  FCVT o1 o2 -> pprPanic "LA64.pprInstr - impossible float conversion" $
                  line (pprOp platform o1 <> text "->" <> pprOp platform o2)
    -- Signed fixed-point convert to floating-point
    -- For LoongArch, ffint.* instructions's second operand must be float-pointing register,
    -- so we need one more operation.
    -- Also to tfint.*.
  SCVTF o1@(OpReg W32 _) o2@(OpReg W32 _) -> lines_
    [
      text "\tmovgr2fr.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2,
      text "\tffint.s.w" <+> pprOp platform o1 <> comma <+> pprOp platform o1
    ]
  SCVTF o1@(OpReg W32 _) o2@(OpReg W64 _) -> lines_
    [
      text "\tmovgr2fr.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2,
      text "\tffint.s.l" <+> pprOp platform o1 <> comma <+> pprOp platform o1
    ]
  SCVTF o1@(OpReg W64 _) o2@(OpReg W32 _) -> lines_
    [
      text "\tmovgr2fr.w" <+> pprOp platform o1 <> comma <+> pprOp platform o2,
      text "\tffint.d.w" <+> pprOp platform o1 <> comma <+> pprOp platform o1
    ]
  SCVTF o1@(OpReg W64 _) o2@(OpReg W64 _) -> lines_
    [
      text "\tmovgr2fr.d" <+> pprOp platform o1 <> comma <+> pprOp platform o2,
      text "\tffint.d.l" <+> pprOp platform o1 <> comma <+> pprOp platform o1
    ]
  SCVTF o1 o2 -> pprPanic "LA64.pprInstr - impossible integer to float conversion" $
                  line (pprOp platform o1 <> text "->" <> pprOp platform o2)

    -- Floating-point convert to signed integer, rounding toward zero
    -- TODO: FCVTZS will destroy src-floating register if the previous opertion
    -- includes this reg. So I'm just stupidly saving and restoring by adding
    -- an extra register.
  FCVTZS o1@(OpReg W32 _) o2@(OpReg W32 _) o3@(OpReg W32 _) -> lines_
    [
      text "\tfmov.s" <+> pprOp platform o2 <> comma <+> pprOp platform o3,
      text "\tftintrz.w.s" <+> pprOp platform o3 <> comma <+> pprOp platform o3,
      text "\tmovfr2gr.s" <+> pprOp platform o1 <> comma <+> pprOp platform o3,
      text "\tfmov.s" <+> pprOp platform o3 <> comma <+> pprOp platform o2
    ]
  FCVTZS o1@(OpReg W32 _) o2@(OpReg W64 _) o3@(OpReg W64 _) -> lines_
    [
      text "\tfmov.d" <+> pprOp platform o2 <> comma <+> pprOp platform o3,
      text "\tftintrz.w.d" <+> pprOp platform o3 <> comma <+> pprOp platform o3,
      text "\tmovfr2gr.s" <+> pprOp platform o1 <> comma <+> pprOp platform o3,
      text "\tfmov.s" <+> pprOp platform o3 <> comma <+> pprOp platform o2
    ]
  FCVTZS o1@(OpReg W64 _) o2@(OpReg W32 _) o3@(OpReg W32 _) -> lines_
    [
      text "\tfmov.s" <+> pprOp platform o2 <> comma <+> pprOp platform o3,
      text "\tftintrz.l.s" <+> pprOp platform o3 <> comma <+> pprOp platform o3,
      text "\tmovfr2gr.d" <+> pprOp platform o1 <> comma <+> pprOp platform o3,
      text "\tfmov.s" <+> pprOp platform o3 <> comma <+> pprOp platform o2
    ]
  FCVTZS o1@(OpReg W64 _) o2@(OpReg W64 _) o3@(OpReg W64 _) -> lines_
    [
      text "\tfmov.d" <+> pprOp platform o2 <> comma <+> pprOp platform o3,
      text "\tftintrz.l.d" <+> pprOp platform o3 <> comma <+> pprOp platform o3,
      text "\tmovfr2gr.d" <+> pprOp platform o1 <> comma <+> pprOp platform o3,
      text "\tfmov.d" <+> pprOp platform o3 <> comma <+> pprOp platform o2
    ]
  FCVTZS o1 o2 o3 -> pprPanic "LA64.pprInstr - impossible float to integer conversion" $
                   line (pprOp platform o3 <> text "->" <+> pprOp platform o1 <+> text "tmpReg:" <+> pprOp platform o2)

  FMIN o1 o2 o3  -> op3 (text "fmin." <> if isSingleOp o2 then text "s" else text "d") o1 o2 o3
  FMINA o1 o2 o3 -> op3 (text "fmina." <> if isSingleOp o2 then text "s" else text "d") o1 o2 o3
  FMAX o1 o2 o3  -> op3 (text "fmax." <> if isSingleOp o2 then text "s" else text "d") o1 o2 o3
  FMAXA o1 o2 o3 -> op3 (text "fmaxa." <> if isSingleOp o2 then text "s" else text "d") o1 o2 o3
  FABS o1 o2 -> op2 (text "fabs." <> if isSingleOp o2 then text "s" else text "d") o1 o2
  FNEG o1 o2 -> op2 (text "fneg." <> if isSingleOp o2 then text "s" else text "d") o1 o2
  FSQRT o1 o2 -> op2 (text "fsqrt." <> if isSingleOp o2 then text "s" else text "d") o1 o2
  FMA variant d o1 o2 o3 ->
    let fma = case variant of
                FMAdd   -> text "\tfmadd." <+> floatPrecission d
                FMSub   -> text "\tfmsub." <+> floatPrecission d
                FNMAdd  -> text "\tfnmadd." <+> floatPrecission d
                FNMSub  -> text "\tfnmsub." <+> floatPrecission d
    in op4 fma d o1 o2 o3

  instr -> panic $ "LA64.pprInstr - Unknown instruction: " ++ (instrCon instr)
  where op2 op o1 o2        = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2
        op3 op o1 o2 o3     = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3
        op4 op o1 o2 o3 o4  = line $ op <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4
{-
    -- TODO: Support dbar with different hints.
    On LoongArch uses "dbar 0" (full completion barrier) for everything.
    But the full completion barrier has no performance to tell, so
    Loongson-3A6000 and newer processors have made finer granularity hints
    available:

    Bit4: ordering or completion (0: completion, 1: ordering)
    Bit3: barrier for previous read (0: true, 1: false)
    Bit2: barrier for previous write (0: true, 1: false)
    Bit1: barrier for succeeding read (0: true, 1: false)
    Bit0: barrier for succeeding write (0: true, 1: false)
-}
        pprBarrierType Hint0 = text "0x0"
        floatPrecission o | isSingleOp o = text "s"
                          | isDoubleOp o = text "d"
                          | otherwise  = pprPanic "Impossible floating point precission: " (pprOp platform o)

-- LoongArch64 Conditional Branch Instructions
pprBcond :: IsLine doc => Cond -> doc
pprBcond c = text "b" <> pprCond c

pprCond :: IsLine doc => Cond -> doc
pprCond c = case c of
      EQ -> text "eq"     -- beq  rj, rd, off16
      NE -> text "ne"     -- bne  rj, rd, off16
      SLT -> text "lt"    -- blt  rj, rd, off16
      SGE -> text "ge"    -- bge  rj, rd, off16
      ULT -> text "ltu"   -- bltu rj, rd, off16
      UGE -> text "geu"   -- bgeu rj, rd, off16
      -- Following not real instructions, just mark it.
      SLE    -> text "sle->ge"   -- ble  rj, rd, off16 -> bge  rd, rj, off16
      SGT    -> text "sgt->lt"   -- bgt  rj, rd, off16 -> blt  rd, rj, off16
      ULE    -> text "ule->geu"  -- bleu rj, rd, off16 -> bgeu rd, rj, off16
      UGT    -> text "ugt->ltu"  -- bgtu rj, rd, off16 -> bltu rd, rj, off16
      _ -> panic $ "LA64.ppr: non-implemented branch condition: " ++ show c