Marge Bot pushed to branch master at Glasgow Haskell Compiler / GHC
Commits:
-
50188615
by Ian Duncan at 2026-05-14T13:45:07+02:00
12 changed files:
- + changelog.d/T26979
- compiler/GHC/CmmToAsm/AArch64/CodeGen.hs
- + testsuite/tests/codeGen/should_gen_asm/aarch64-shl-subword.asm
- + testsuite/tests/codeGen/should_gen_asm/aarch64-shl-subword.hs
- + testsuite/tests/codeGen/should_gen_asm/aarch64-ushr-subword.asm
- + testsuite/tests/codeGen/should_gen_asm/aarch64-ushr-subword.hs
- testsuite/tests/codeGen/should_gen_asm/all.T
- + testsuite/tests/codeGen/should_run/aarch64-subword-ops.hs
- + testsuite/tests/codeGen/should_run/aarch64-subword-ops.stdout
- + testsuite/tests/codeGen/should_run/aarch64-ushr-subword-run.hs
- + testsuite/tests/codeGen/should_run/aarch64-ushr-subword-run.stdout
- testsuite/tests/codeGen/should_run/all.T
Changes:
| 1 | +section: compiler
|
|
| 2 | +issues: #26979
|
|
| 3 | +mrs: !15620
|
|
| 4 | +synopsis:
|
|
| 5 | + On AArch64, use an arithmetic instead of a logical right shift when
|
|
| 6 | + sign extending at 8/16 bit word size.
|
|
| 7 | +description:
|
|
| 8 | + The unsigned right shift ``MO_U_Shr`` for sub-word widths (``W8``, ``W16``)
|
|
| 9 | + with a variable shift amount now correctly emits ``LSR`` instead of ``ASR``.
|
|
| 10 | + ``ASR`` was semantically wrong, and would break if the zero-extension were
|
|
| 11 | + ever optimized away. |
| ... | ... | @@ -661,6 +661,15 @@ opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w) |
| 661 | 661 | -- sub-word-size value always contains the zero-extended form of that value
|
| 662 | 662 | -- in between operations.
|
| 663 | 663 | --
|
| 664 | +-- IMPORTANT: this invariant only holds within a single expression tree as
|
|
| 665 | +-- generated by the NCG (via truncateReg after each sub-word operation). It
|
|
| 666 | +-- does NOT hold at function entry points or across basic block boundaries,
|
|
| 667 | +-- because the GHC calling convention does not guarantee that callers
|
|
| 668 | +-- zero-extend sub-word arguments. Therefore, any operation that is sensitive
|
|
| 669 | +-- to the upper bits of its input (e.g. unsigned right shift, unsigned
|
|
| 670 | +-- division) must explicitly zero- or sign-extend its operands rather than
|
|
| 671 | +-- assuming they are already extended.
|
|
| 672 | +--
|
|
| 664 | 673 | -- For instance, consider the program,
|
| 665 | 674 | --
|
| 666 | 675 | -- test(bits64 buffer)
|
| ... | ... | @@ -679,7 +688,7 @@ opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w) |
| 679 | 688 | -- Next we compute `c`: The `%not` requires no extension of its operands, but
|
| 680 | 689 | -- we must still truncate the result back down to 8-bits. Finally the `%shrl`
|
| 681 | 690 | -- requires no extension and no truncate since we can assume that
|
| 682 | --- `c` is zero-extended.
|
|
| 691 | +-- `c` is zero-extended (it was produced by a truncateReg in the same block).
|
|
| 683 | 692 | --
|
| 684 | 693 | -- TODO:
|
| 685 | 694 | -- Don't use Width in Operands
|
| ... | ... | @@ -1084,17 +1093,30 @@ getRegister' config plat expr |
| 1084 | 1093 | CmmMachOp (MO_U_Quot w) [x, y] | w == W8 -> do
|
| 1085 | 1094 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1086 | 1095 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1087 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 1088 | - (UXTB (OpReg w reg_y) (OpReg w reg_y)) `snocOL`
|
|
| 1089 | - (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 1096 | + tmp_x <- getNewRegNat (intFormat w)
|
|
| 1097 | + tmp_y <- getNewRegNat (intFormat w)
|
|
| 1098 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
| 1099 | + (UXTB (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
| 1100 | + (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
| 1090 | 1101 | CmmMachOp (MO_U_Quot w) [x, y] | w == W16 -> do
|
| 1091 | 1102 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1092 | 1103 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1093 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 1094 | - (UXTH (OpReg w reg_y) (OpReg w reg_y)) `snocOL`
|
|
| 1095 | - (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 1104 | + tmp_x <- getNewRegNat (intFormat w)
|
|
| 1105 | + tmp_y <- getNewRegNat (intFormat w)
|
|
| 1106 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
| 1107 | + (UXTH (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
| 1108 | + (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
| 1096 | 1109 | |
| 1097 | 1110 | -- 2. Shifts. x << n, x >> n.
|
| 1111 | + -- Sub-word left shifts by a constant: use UBFM (UBFIZ alias) to shift
|
|
| 1112 | + -- and mask in a single instruction. See Note [Signed arithmetic on AArch64].
|
|
| 1113 | + CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
|
|
| 1114 | + (reg_x, _format_x, code_x) <- getSomeReg x
|
|
| 1115 | + return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (UBFM (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger ((32 - n) `mod` 32))) (OpImm (ImmInteger (7 - n)))))
|
|
| 1116 | + CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
|
| 1117 | + (reg_x, _format_x, code_x) <- getSomeReg x
|
|
| 1118 | + return $ Any (intFormat w) (\dst -> code_x `snocOL` annExpr expr (UBFM (OpReg w dst) (OpReg w reg_x) (OpImm (ImmInteger ((32 - n) `mod` 32))) (OpImm (ImmInteger (15 - n)))))
|
|
| 1119 | + |
|
| 1098 | 1120 | CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))]
|
| 1099 | 1121 | | w == W32 || w == W64
|
| 1100 | 1122 | , 0 <= n, n < fromIntegral (widthInBits w) -> do
|
| ... | ... | @@ -1108,8 +1130,11 @@ getRegister' config plat expr |
| 1108 | 1130 | CmmMachOp (MO_S_Shr w) [x, y] | w == W8 -> do
|
| 1109 | 1131 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1110 | 1132 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1111 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 1112 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)) `snocOL`
|
|
| 1133 | + -- Use a temporary register to avoid sign-extending reg_x in-place,
|
|
| 1134 | + -- as other operations may use reg_x.
|
|
| 1135 | + tmp <- getNewRegNat (intFormat w)
|
|
| 1136 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
| 1137 | + (ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
| 1113 | 1138 | (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
| 1114 | 1139 | |
| 1115 | 1140 | CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
| ... | ... | @@ -1119,8 +1144,11 @@ getRegister' config plat expr |
| 1119 | 1144 | CmmMachOp (MO_S_Shr w) [x, y] | w == W16 -> do
|
| 1120 | 1145 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1121 | 1146 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1122 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 1123 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)) `snocOL`
|
|
| 1147 | + -- Use a temporary register to avoid sign-extending reg_x in-place,
|
|
| 1148 | + -- as other operations may use reg_x.
|
|
| 1149 | + tmp <- getNewRegNat (intFormat w)
|
|
| 1150 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
| 1151 | + (ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
| 1124 | 1152 | (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
| 1125 | 1153 | |
| 1126 | 1154 | CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))]
|
| ... | ... | @@ -1135,8 +1163,8 @@ getRegister' config plat expr |
| 1135 | 1163 | CmmMachOp (MO_U_Shr w) [x, y] | w == W8 -> do
|
| 1136 | 1164 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1137 | 1165 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1138 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 1139 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 1166 | + tmp <- getNewRegNat (intFormat w)
|
|
| 1167 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` UXTB (OpReg w tmp) (OpReg w reg_x) `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)))
|
|
| 1140 | 1168 | |
| 1141 | 1169 | CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
| 1142 | 1170 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| ... | ... | @@ -1144,8 +1172,8 @@ getRegister' config plat expr |
| 1144 | 1172 | CmmMachOp (MO_U_Shr w) [x, y] | w == W16 -> do
|
| 1145 | 1173 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 1146 | 1174 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 1147 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x))
|
|
| 1148 | - `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 1175 | + tmp <- getNewRegNat (intFormat w)
|
|
| 1176 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` UXTH (OpReg w tmp) (OpReg w reg_x) `snocOL` annExpr expr (LSR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)))
|
|
| 1149 | 1177 | |
| 1150 | 1178 | CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))]
|
| 1151 | 1179 | | w == W32 || w == W64
|
| 1 | +ubfm |
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +module ShlSubWord (shlW8) where
|
|
| 3 | + |
|
| 4 | +import GHC.Exts
|
|
| 5 | +import GHC.Word
|
|
| 6 | + |
|
| 7 | +shlW8 :: Word8 -> Word8
|
|
| 8 | +shlW8 (W8# w) = W8# (uncheckedShiftLWord8# w 4#) |
| 1 | +lsr |
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +module UShrSubWord (ushrW8) where
|
|
| 3 | + |
|
| 4 | +import GHC.Exts
|
|
| 5 | +import GHC.Word
|
|
| 6 | + |
|
| 7 | +ushrW8 :: Word8 -> Int -> Word8
|
|
| 8 | +ushrW8 x n = x `shiftR` n
|
|
| 9 | + where shiftR (W8# w) (I# i) = W8# (wordToWord8# (word8ToWord# w `uncheckedShiftRL#` i)) |
| ... | ... | @@ -23,3 +23,11 @@ test('avx512-int64-minmax', [unless(arch('x86_64'), skip), |
| 23 | 23 | when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512vl'])
|
| 24 | 24 | test('avx512-word64-minmax', [unless(arch('x86_64'), skip),
|
| 25 | 25 | when(unregisterised(), skip)], compile_grep_asm, ['hs', True, '-mavx512vl'])
|
| 26 | +is_aarch64_codegen = [
|
|
| 27 | + unless(arch('aarch64'), skip),
|
|
| 28 | + when(unregisterised(), skip),
|
|
| 29 | +]
|
|
| 30 | + |
|
| 31 | +# AArch64-specific tests
|
|
| 32 | +test('aarch64-ushr-subword', is_aarch64_codegen, compile_grep_asm, ['hs', True, '-O'])
|
|
| 33 | +test('aarch64-shl-subword', is_aarch64_codegen, compile_grep_asm, ['hs', True, '-O']) |
| 1 | +{-# LANGUAGE MagicHash #-}
|
|
| 2 | +module Main where
|
|
| 3 | + |
|
| 4 | +import GHC.Exts
|
|
| 5 | +import GHC.Word
|
|
| 6 | +import GHC.Int
|
|
| 7 | + |
|
| 8 | +-- Uses sub-word primops directly so that the NCG sees MO_Shl W8,
|
|
| 9 | +-- MO_U_Shr W8, MO_S_Shr W8 etc. (the Bits class widens to Word#/Int#).
|
|
| 10 | + |
|
| 11 | +-- NOINLINE to prevent constant folding.
|
|
| 12 | + |
|
| 13 | +-- MO_U_Shr W8/W16 variable shift
|
|
| 14 | +{-# NOINLINE ushrW8 #-}
|
|
| 15 | +ushrW8 :: Word8 -> Int -> Word8
|
|
| 16 | +ushrW8 (W8# w) (I# i) = W8# (uncheckedShiftRLWord8# w i)
|
|
| 17 | + |
|
| 18 | +{-# NOINLINE ushrW16 #-}
|
|
| 19 | +ushrW16 :: Word16 -> Int -> Word16
|
|
| 20 | +ushrW16 (W16# w) (I# i) = W16# (uncheckedShiftRLWord16# w i)
|
|
| 21 | + |
|
| 22 | +-- MO_S_Shr W8/W16 variable shift
|
|
| 23 | +{-# NOINLINE sshrI8 #-}
|
|
| 24 | +sshrI8 :: Int8 -> Int -> Int8
|
|
| 25 | +sshrI8 (I8# x) (I# i) = I8# (uncheckedShiftRAInt8# x i)
|
|
| 26 | + |
|
| 27 | +{-# NOINLINE sshrI16 #-}
|
|
| 28 | +sshrI16 :: Int16 -> Int -> Int16
|
|
| 29 | +sshrI16 (I16# x) (I# i) = I16# (uncheckedShiftRAInt16# x i)
|
|
| 30 | + |
|
| 31 | +-- MO_Shl W8/W16 variable shift
|
|
| 32 | +{-# NOINLINE shlW8 #-}
|
|
| 33 | +shlW8 :: Word8 -> Int -> Word8
|
|
| 34 | +shlW8 (W8# w) (I# i) = W8# (uncheckedShiftLWord8# w i)
|
|
| 35 | + |
|
| 36 | +{-# NOINLINE shlW16 #-}
|
|
| 37 | +shlW16 :: Word16 -> Int -> Word16
|
|
| 38 | +shlW16 (W16# w) (I# i) = W16# (uncheckedShiftLWord16# w i)
|
|
| 39 | + |
|
| 40 | +-- quot exercising MO_U_Quot W8/W16
|
|
| 41 | +{-# NOINLINE quotW8 #-}
|
|
| 42 | +quotW8 :: Word8 -> Word8 -> Word8
|
|
| 43 | +quotW8 (W8# x) (W8# y) = W8# (quotWord8# x y)
|
|
| 44 | + |
|
| 45 | +{-# NOINLINE quotW16 #-}
|
|
| 46 | +quotW16 :: Word16 -> Word16 -> Word16
|
|
| 47 | +quotW16 (W16# x) (W16# y) = W16# (quotWord16# x y)
|
|
| 48 | + |
|
| 49 | +-- Register clobbering: use a value both in a shift/quot and afterward.
|
|
| 50 | +-- If the sign/zero extension clobbers the source register, the second
|
|
| 51 | +-- use sees the wrong value.
|
|
| 52 | + |
|
| 53 | +{-# NOINLINE sshrAndAdd8 #-}
|
|
| 54 | +sshrAndAdd8 :: Int8 -> Int -> Int8
|
|
| 55 | +sshrAndAdd8 a n = sshrI8 a n + a
|
|
| 56 | + |
|
| 57 | +{-# NOINLINE sshrAndAdd16 #-}
|
|
| 58 | +sshrAndAdd16 :: Int16 -> Int -> Int16
|
|
| 59 | +sshrAndAdd16 a n = sshrI16 a n + a
|
|
| 60 | + |
|
| 61 | +{-# NOINLINE quotAndAdd8 #-}
|
|
| 62 | +quotAndAdd8 :: Word8 -> Word8 -> Word8
|
|
| 63 | +quotAndAdd8 a b = quotW8 a b + a + b
|
|
| 64 | + |
|
| 65 | +{-# NOINLINE quotAndAdd16 #-}
|
|
| 66 | +quotAndAdd16 :: Word16 -> Word16 -> Word16
|
|
| 67 | +quotAndAdd16 a b = quotW16 a b + a + b
|
|
| 68 | + |
|
| 69 | +main :: IO ()
|
|
| 70 | +main = do
|
|
| 71 | + putStrLn "-- MO_U_Shr variable shift"
|
|
| 72 | + print (ushrW8 0x80 1) -- 64
|
|
| 73 | + print (ushrW8 0xFF 4) -- 15
|
|
| 74 | + print (ushrW8 0x42 0) -- 66
|
|
| 75 | + print (ushrW16 0x8000 1) -- 16384
|
|
| 76 | + print (ushrW16 0xFFFF 8) -- 255
|
|
| 77 | + print (ushrW16 0x1234 0) -- 4660
|
|
| 78 | + |
|
| 79 | + putStrLn "-- MO_S_Shr variable shift"
|
|
| 80 | + print (sshrI8 (-1) 1) -- -1
|
|
| 81 | + print (sshrI8 (-128) 1) -- -64
|
|
| 82 | + print (sshrI8 127 1) -- 63
|
|
| 83 | + print (sshrI8 0x42 3) -- 8
|
|
| 84 | + print (sshrI16 (-1) 1) -- -1
|
|
| 85 | + print (sshrI16 (-32768) 1) -- -16384
|
|
| 86 | + print (sshrI16 32767 8) -- 127
|
|
| 87 | + |
|
| 88 | + putStrLn "-- MO_Shl variable shift"
|
|
| 89 | + print (shlW8 0x01 0) -- 1
|
|
| 90 | + print (shlW8 0x01 4) -- 16
|
|
| 91 | + print (shlW8 0xFF 1) -- 254
|
|
| 92 | + print (shlW8 0x42 3) -- 16
|
|
| 93 | + print (shlW16 0x0001 0) -- 1
|
|
| 94 | + print (shlW16 0x0001 8) -- 256
|
|
| 95 | + print (shlW16 0xFFFF 1) -- 65534
|
|
| 96 | + print (shlW16 0x1234 4) -- 9024
|
|
| 97 | + |
|
| 98 | + putStrLn "-- MO_U_Quot"
|
|
| 99 | + print (quotW8 255 10) -- 25
|
|
| 100 | + print (quotW8 200 7) -- 28
|
|
| 101 | + print (quotW8 1 1) -- 1
|
|
| 102 | + print (quotW16 65535 256) -- 255
|
|
| 103 | + print (quotW16 1000 3) -- 333
|
|
| 104 | + |
|
| 105 | + putStrLn "-- register clobbering: shift + reuse"
|
|
| 106 | + print (sshrAndAdd8 (-128) 1) -- 64 (wraps: -64 + -128 = -192 = 64 as Int8)
|
|
| 107 | + print (sshrAndAdd8 0x42 1) -- 99
|
|
| 108 | + print (sshrAndAdd16 (-32768) 1) -- 16384 (wraps)
|
|
| 109 | + print (sshrAndAdd16 0x1234 4) -- 4951
|
|
| 110 | + |
|
| 111 | + putStrLn "-- register clobbering: quot + reuse"
|
|
| 112 | + print (quotAndAdd8 200 7) -- 235
|
|
| 113 | + print (quotAndAdd8 255 10) -- 34 (wraps: 290 mod 256)
|
|
| 114 | + print (quotAndAdd16 1000 3) -- 1336
|
|
| 115 | + print (quotAndAdd16 65535 256) -- 510 (wraps: 66046 mod 65536) |
| 1 | +-- MO_U_Shr variable shift
|
|
| 2 | +64
|
|
| 3 | +15
|
|
| 4 | +66
|
|
| 5 | +16384
|
|
| 6 | +255
|
|
| 7 | +4660
|
|
| 8 | +-- MO_S_Shr variable shift
|
|
| 9 | +-1
|
|
| 10 | +-64
|
|
| 11 | +63
|
|
| 12 | +8
|
|
| 13 | +-1
|
|
| 14 | +-16384
|
|
| 15 | +127
|
|
| 16 | +-- MO_Shl variable shift
|
|
| 17 | +1
|
|
| 18 | +16
|
|
| 19 | +254
|
|
| 20 | +16
|
|
| 21 | +1
|
|
| 22 | +256
|
|
| 23 | +65534
|
|
| 24 | +9024
|
|
| 25 | +-- MO_U_Quot
|
|
| 26 | +25
|
|
| 27 | +28
|
|
| 28 | +1
|
|
| 29 | +255
|
|
| 30 | +333
|
|
| 31 | +-- register clobbering: shift + reuse
|
|
| 32 | +64
|
|
| 33 | +99
|
|
| 34 | +16384
|
|
| 35 | +4951
|
|
| 36 | +-- register clobbering: quot + reuse
|
|
| 37 | +235
|
|
| 38 | +34
|
|
| 39 | +1336
|
|
| 40 | +510 |
| 1 | +import Data.Bits (shiftR)
|
|
| 2 | +import Data.Word (Word8, Word16)
|
|
| 3 | + |
|
| 4 | +main :: IO ()
|
|
| 5 | +main = do
|
|
| 6 | + print (shiftR (0x80 :: Word8) 1)
|
|
| 7 | + print (shiftR (0xFF :: Word8) 4)
|
|
| 8 | + print (shiftR (0x8000 :: Word16) 1)
|
|
| 9 | + print (shiftR (0xFFFF :: Word16) 8) |
| 1 | +64
|
|
| 2 | +15
|
|
| 3 | +16384
|
|
| 4 | +255 |
| ... | ... | @@ -279,3 +279,6 @@ test('T27072d', [req_c, only_ways(['dyn']), when(not opsys('darwin'), skip), |
| 279 | 279 | # Skipped on Darwin (Apple linker doesn't support --wrap).
|
| 280 | 280 | test('T27072w', [req_c, js_skip, when(opsys('darwin'), skip)],
|
| 281 | 281 | compile_and_run, ['T27072w_c.c -no-hs-main -optl-Wl,--wrap=hs_spt_remove'])
|
| 282 | +# AArch64-specific runtime tests
|
|
| 283 | +test('aarch64-ushr-subword-run', [unless(arch('aarch64'), skip)], compile_and_run, ['-O'])
|
|
| 284 | +test('aarch64-subword-ops', [unless(arch('aarch64'), skip)], compile_and_run, ['-O']) |