summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMoritz Angermann <moritz.angermann@gmail.com>2021-06-17 22:35:14 +0800
committerMarge Bot <ben+marge-bot@smart-cactus.org>2021-06-23 20:34:23 -0400
commitd79530d17bc9de61a8bda58308c0377d1c3c697b (patch)
tree414db10555bdedbc81d52b0bd3a4ffb5a1cc144e
parente2d8023dfe1f2f981fc77fe3decd907352592f26 (diff)
downloadhaskell-d79530d17bc9de61a8bda58308c0377d1c3c697b.tar.gz
[aarch64 NCG] Add better support for sub-word primops
During the intial NCG development, GHC did not have support for anything below Words. As such the NCG didn't support any of this either. AArch64-Darwin however needs support for subword, as arguments in excess of the first eight (8) passed via registers are passed on the stack, and there in a packed fashion. Thus ghc learned about subword sizes. This than lead us to gain subword primops, and these subsequently highlighted deficiencies in the AArch64 NCG. This patch rectifies the ones I found through via the test-suite. I do not claim this to be exhaustive. Fixes: #19993 Metric Increase: T10421 T13035 T13719 T14697 T1969 T9203 T9872a T9872b T9872c T9872d T9961 haddock.Cabal haddock.base parsing001
-rw-r--r--compiler/GHC/CmmToAsm/AArch64/CodeGen.hs152
-rw-r--r--compiler/GHC/CmmToAsm/AArch64/Instr.hs25
-rw-r--r--compiler/GHC/CmmToAsm/AArch64/Ppr.hs8
3 files changed, 150 insertions, 35 deletions
diff --git a/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs b/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
index b0984070fc..f637aeba90 100644
--- a/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
+++ b/compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
@@ -624,6 +624,15 @@ getRegister' config plat expr
where w' = formatToWidth (cmmTypeFormat (cmmRegType plat reg))
r' = getRegisterReg plat reg
+ CmmMachOp (MO_U_Quot w) [x, y] | w == W8 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (UXTB (OpReg w reg_y) (OpReg w reg_y)) `snocOL` (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+ CmmMachOp (MO_U_Quot w) [x, y] | w == W16 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (UXTH (OpReg w reg_y) (OpReg w reg_y)) `snocOL` (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+
-- 2. Shifts. x << n, x >> n.
CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W32, 0 <= n, n < 32 -> do
(reg_x, _format_x, code_x) <- getSomeReg x
@@ -632,9 +641,51 @@ getRegister' config plat expr
(reg_x, _format_x, code_x) <- getSomeReg x
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (LSL (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
+ CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (SBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (8-n)))))
+ CmmMachOp (MO_S_Shr w) [x, y] | w == W8 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+
+ CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (SBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (16-n)))))
+ CmmMachOp (MO_S_Shr w) [x, y] | w == W16 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+
+ CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W32, 0 <= n, n < 32 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (ASR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
+
+ CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W64, 0 <= n, n < 64 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (ASR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
+
+
+ CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (UBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (8-n)))))
+ CmmMachOp (MO_U_Shr w) [x, y] | w == W8 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+
+ CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (UBFX (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n)) (OpImm (ImmInteger (16-n)))))
+ CmmMachOp (MO_U_Shr w) [x, y] | w == W16 -> do
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
+
CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W32, 0 <= n, n < 32 -> do
(reg_x, _format_x, code_x) <- getSomeReg x
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
+
CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W64, 0 <= n, n < 64 -> do
(reg_x, _format_x, code_x) <- getSomeReg x
return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger n))))
@@ -687,6 +738,13 @@ getRegister' config plat expr
-- and thus we end up with <float> + <float> => MO_Add <float> <float>
MO_Add w -> genOp w (\d x y -> unitOL $ annExpr expr (ADD d x y))
MO_Sub w -> genOp w (\d x y -> unitOL $ annExpr expr (SUB d x y))
+
+ -- Note [CSET]
+ --
+ -- Setting conditional flags: the architecture internally knows the
+ -- following flag bits. And based on thsoe comparisons as in the
+ -- table below.
+ --
-- 31 30 29 28
-- .---+---+---+---+-- - -
-- | N | Z | C | V |
@@ -718,10 +776,13 @@ getRegister' config plat expr
-- | AL | Always | Any | 1110 |
-- | NV | Never | Any | 1111 |
--- '-------------------------------------------------------------------------'
-
- MO_Eq w -> intOp w (\d x y -> toOL [ CMP x y, CSET d EQ ])
- MO_Ne w -> intOp w (\d x y -> toOL [ CMP x y, CSET d NE ])
- MO_Mul w -> intOp w (\d x y -> unitOL $ MUL d x y)
+ MO_Eq w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d EQ ])
+ MO_Eq w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d EQ ])
+ MO_Eq w -> intOp w (\d x y -> toOL [ CMP x y, CSET d EQ ])
+ MO_Ne w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d NE ])
+ MO_Ne w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d NE ])
+ MO_Ne w -> intOp w (\d x y -> toOL [ CMP x y, CSET d NE ])
+ MO_Mul w -> intOp w (\d x y -> unitOL $ MUL d x y)
-- Signed multiply/divide
MO_S_MulMayOflo w -> intOp w (\d x y -> toOL [ MUL d x y, CSET d VS ])
@@ -743,17 +804,33 @@ getRegister' config plat expr
MO_U_Rem w -> withTempIntReg w $ \t ->
intOp w (\d x y -> toOL [ UDIV t x y, MSUB d t y x ])
- -- Signed comparisons -- see above for the CSET discussion
- MO_S_Ge w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SGE ])
- MO_S_Le w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SLE ])
- MO_S_Gt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SGT ])
- MO_S_Lt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SLT ])
+ -- Signed comparisons -- see Note [CSET]
+ MO_S_Ge w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d SGE ])
+ MO_S_Ge w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d SGE ])
+ MO_S_Ge w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SGE ])
+ MO_S_Le w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d SLE ])
+ MO_S_Le w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d SLE ])
+ MO_S_Le w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SLE ])
+ MO_S_Gt w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d SGT ])
+ MO_S_Gt w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d SGT ])
+ MO_S_Gt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SGT ])
+ MO_S_Lt w@W8 -> intOp w (\d x y -> toOL [ SXTB x x, SXTB y y, CMP x y, CSET d SLT ])
+ MO_S_Lt w@W16 -> intOp w (\d x y -> toOL [ SXTH x x, SXTH y y, CMP x y, CSET d SLT ])
+ MO_S_Lt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d SLT ])
-- Unsigned comparisons
- MO_U_Ge w -> intOp w (\d x y -> toOL [ CMP x y, CSET d UGE ])
- MO_U_Le w -> intOp w (\d x y -> toOL [ CMP x y, CSET d ULE ])
- MO_U_Gt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d UGT ])
- MO_U_Lt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d ULT ])
+ MO_U_Ge w@W8 -> intOp w (\d x y -> toOL [ UXTB x x, UXTB y y, CMP x y, CSET d UGE ])
+ MO_U_Ge w@W16 -> intOp w (\d x y -> toOL [ UXTH x x, UXTH y y, CMP x y, CSET d UGE ])
+ MO_U_Ge w -> intOp w (\d x y -> toOL [ CMP x y, CSET d UGE ])
+ MO_U_Le w@W8 -> intOp w (\d x y -> toOL [ UXTB x x, UXTB y y, CMP x y, CSET d ULE ])
+ MO_U_Le w@W16 -> intOp w (\d x y -> toOL [ UXTH x x, UXTH y y, CMP x y, CSET d ULE ])
+ MO_U_Le w -> intOp w (\d x y -> toOL [ CMP x y, CSET d ULE ])
+ MO_U_Gt w@W8 -> intOp w (\d x y -> toOL [ UXTB x x, UXTB y y, CMP x y, CSET d UGT ])
+ MO_U_Gt w@W16 -> intOp w (\d x y -> toOL [ UXTH x x, UXTH y y, CMP x y, CSET d UGT ])
+ MO_U_Gt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d UGT ])
+ MO_U_Lt w@W8 -> intOp w (\d x y -> toOL [ UXTB x x, UXTB y y, CMP x y, CSET d ULT ])
+ MO_U_Lt w@W16 -> intOp w (\d x y -> toOL [ UXTH x x, UXTH y y, CMP x y, CSET d ULT ])
+ MO_U_Lt w -> intOp w (\d x y -> toOL [ CMP x y, CSET d ULT ])
-- Floating point arithmetic
MO_F_Add w -> floatOp w (\d x y -> unitOL $ ADD d x y)
@@ -935,11 +1012,28 @@ genCondJump bid expr = do
-- Generic case.
CmmMachOp mop [x, y] -> do
- let bcond w cmp = do
- -- compute both sides.
- (reg_x, _format_x, code_x) <- getSomeReg x
- (reg_y, _format_y, code_y) <- getSomeReg y
- return $ code_x `appOL` code_y `snocOL` CMP (OpReg w reg_x) (OpReg w reg_y) `snocOL` (annExpr expr (BCOND cmp (TBlock bid)))
+ let ubcond w cmp = do
+ -- compute both sides.
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ let x' = OpReg w reg_x
+ y' = OpReg w reg_y
+ return $ case w of
+ W8 -> code_x `appOL` code_y `appOL` toOL [ UXTB x' x', UXTB y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+ W16 -> code_x `appOL` code_y `appOL` toOL [ UXTH x' x', UXTH y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+ _ -> code_x `appOL` code_y `appOL` toOL [ CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+
+ sbcond w cmp = do
+ -- compute both sides.
+ (reg_x, _format_x, code_x) <- getSomeReg x
+ (reg_y, _format_y, code_y) <- getSomeReg y
+ let x' = OpReg w reg_x
+ y' = OpReg w reg_y
+ return $ case w of
+ W8 -> code_x `appOL` code_y `appOL` toOL [ SXTB x' x', SXTB y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+ W16 -> code_x `appOL` code_y `appOL` toOL [ SXTH x' x', SXTH y' y', CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+ _ -> code_x `appOL` code_y `appOL` toOL [ CMP x' y', (annExpr expr (BCOND cmp (TBlock bid))) ]
+
fbcond w cmp = do
-- ensure we get float regs
(reg_fx, _format_fx, code_fx) <- getFloatReg x
@@ -955,17 +1049,17 @@ genCondJump bid expr = do
MO_F_Lt w -> fbcond w OLT
MO_F_Le w -> fbcond w OLE
- MO_Eq w -> bcond w EQ
- MO_Ne w -> bcond w NE
-
- MO_S_Gt w -> bcond w SGT
- MO_S_Ge w -> bcond w SGE
- MO_S_Lt w -> bcond w SLT
- MO_S_Le w -> bcond w SLE
- MO_U_Gt w -> bcond w UGT
- MO_U_Ge w -> bcond w UGE
- MO_U_Lt w -> bcond w ULT
- MO_U_Le w -> bcond w ULE
+ MO_Eq w -> sbcond w EQ
+ MO_Ne w -> sbcond w NE
+
+ MO_S_Gt w -> sbcond w SGT
+ MO_S_Ge w -> sbcond w SGE
+ MO_S_Lt w -> sbcond w SLT
+ MO_S_Le w -> sbcond w SLE
+ MO_U_Gt w -> ubcond w UGT
+ MO_U_Ge w -> ubcond w UGE
+ MO_U_Lt w -> ubcond w ULT
+ MO_U_Le w -> ubcond w ULE
_ -> pprPanic "AArch64.genCondJump:case mop: " (text $ show expr)
_ -> pprPanic "AArch64.genCondJump: " (text $ show expr)
diff --git a/compiler/GHC/CmmToAsm/AArch64/Instr.hs b/compiler/GHC/CmmToAsm/AArch64/Instr.hs
index 7d4eaa95f6..6318441b07 100644
--- a/compiler/GHC/CmmToAsm/AArch64/Instr.hs
+++ b/compiler/GHC/CmmToAsm/AArch64/Instr.hs
@@ -87,7 +87,12 @@ regUsageOfInstr platform instr = case instr of
-- 2. Bit Manipulation Instructions ------------------------------------------
SBFM dst src _ _ -> usage (regOp src, regOp dst)
UBFM dst src _ _ -> usage (regOp src, regOp dst)
-
+ SBFX dst src _ _ -> usage (regOp src, regOp dst)
+ UBFX dst src _ _ -> usage (regOp src, regOp dst)
+ SXTB dst src -> usage (regOp src, regOp dst)
+ UXTB dst src -> usage (regOp src, regOp dst)
+ SXTH dst src -> usage (regOp src, regOp dst)
+ UXTH dst src -> usage (regOp src, regOp dst)
-- 3. Logical and Move Instructions ------------------------------------------
AND dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
ASR dst src1 src2 -> usage (regOp src1 ++ regOp src2, regOp dst)
@@ -210,6 +215,12 @@ patchRegsOfInstr instr env = case instr of
-- 2. Bit Manipulation Instructions ----------------------------------------
SBFM o1 o2 o3 o4 -> SBFM (patchOp o1) (patchOp o2) (patchOp o3) (patchOp o4)
UBFM o1 o2 o3 o4 -> UBFM (patchOp o1) (patchOp o2) (patchOp o3) (patchOp o4)
+ SBFX o1 o2 o3 o4 -> SBFX (patchOp o1) (patchOp o2) (patchOp o3) (patchOp o4)
+ UBFX o1 o2 o3 o4 -> UBFX (patchOp o1) (patchOp o2) (patchOp o3) (patchOp o4)
+ SXTB o1 o2 -> SXTB (patchOp o1) (patchOp o2)
+ UXTB o1 o2 -> UXTB (patchOp o1) (patchOp o2)
+ SXTH o1 o2 -> SXTH (patchOp o1) (patchOp o2)
+ UXTH o1 o2 -> UXTH (patchOp o1) (patchOp o2)
-- 3. Logical and Move Instructions ----------------------------------------
AND o1 o2 o3 -> AND (patchOp o1) (patchOp o2) (patchOp o3)
@@ -518,11 +529,10 @@ data Instr
| DELTA Int
-- 0. Pseudo Instructions --------------------------------------------------
- -- These are instructions not contained or only partially contained in the
- -- official ISA, but make reading clearer. Some of them might even be
- -- implemented in the assembler, but are not guaranteed to be portable.
- -- | SXTB Operand Operand
- -- | SXTH Operand Operand
+ | SXTB Operand Operand
+ | UXTB Operand Operand
+ | SXTH Operand Operand
+ | UXTH Operand Operand
-- | SXTW Operand Operand
-- | SXTX Operand Operand
| PUSH_STACK_FRAME
@@ -569,6 +579,9 @@ data Instr
| UBFM Operand Operand Operand Operand -- rd = rn[i,j]
-- UXTB = UBFM <Wd>, <Wn>, #0, #7
-- UXTH = UBFM <Wd>, <Wn>, #0, #15
+ -- Signed/Unsigned bitfield extract
+ | SBFX Operand Operand Operand Operand -- rd = rn[i,j]
+ | UBFX Operand Operand Operand Operand -- rd = rn[i,j]
-- 3. Logical and Move Instructions ----------------------------------------
| AND Operand Operand Operand -- rd = rn & op2
diff --git a/compiler/GHC/CmmToAsm/AArch64/Ppr.hs b/compiler/GHC/CmmToAsm/AArch64/Ppr.hs
index 3f413339c2..6a799f2e9a 100644
--- a/compiler/GHC/CmmToAsm/AArch64/Ppr.hs
+++ b/compiler/GHC/CmmToAsm/AArch64/Ppr.hs
@@ -423,6 +423,14 @@ pprInstr platform instr = case instr of
-- 2. Bit Manipulation Instructions ------------------------------------------
SBFM o1 o2 o3 o4 -> text "\tsbfm" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4
UBFM o1 o2 o3 o4 -> text "\tubfm" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4
+ -- signed and unsigned bitfield extract
+ SBFX o1 o2 o3 o4 -> text "\tsbfx" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4
+ UBFX o1 o2 o3 o4 -> text "\tubfx" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3 <> comma <+> pprOp platform o4
+ SXTB o1 o2 -> text "\tsxtb" <+> pprOp platform o1 <> comma <+> pprOp platform o2
+ UXTB o1 o2 -> text "\tuxtb" <+> pprOp platform o1 <> comma <+> pprOp platform o2
+ SXTH o1 o2 -> text "\tsxth" <+> pprOp platform o1 <> comma <+> pprOp platform o2
+ UXTH o1 o2 -> text "\tuxth" <+> pprOp platform o1 <> comma <+> pprOp platform o2
+
-- 3. Logical and Move Instructions ------------------------------------------
AND o1 o2 o3 -> text "\tand" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3
ANDS o1 o2 o3 -> text "\tands" <+> pprOp platform o1 <> comma <+> pprOp platform o2 <> comma <+> pprOp platform o3