Magnus pushed to branch wip/mangoiv/ghc-9.12-bp at Glasgow Haskell Compiler / GHC
Commits:
-
82c521c6
by Ian Duncan at 2026-05-25T13:39:24+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. |
| ... | ... | @@ -525,6 +525,15 @@ opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w) |
| 525 | 525 | -- sub-word-size value always contains the zero-extended form of that value
|
| 526 | 526 | -- in between operations.
|
| 527 | 527 | --
|
| 528 | +-- IMPORTANT: this invariant only holds within a single expression tree as
|
|
| 529 | +-- generated by the NCG (via truncateReg after each sub-word operation). It
|
|
| 530 | +-- does NOT hold at function entry points or across basic block boundaries,
|
|
| 531 | +-- because the GHC calling convention does not guarantee that callers
|
|
| 532 | +-- zero-extend sub-word arguments. Therefore, any operation that is sensitive
|
|
| 533 | +-- to the upper bits of its input (e.g. unsigned right shift, unsigned
|
|
| 534 | +-- division) must explicitly zero- or sign-extend its operands rather than
|
|
| 535 | +-- assuming they are already extended.
|
|
| 536 | +--
|
|
| 528 | 537 | -- For instance, consider the program,
|
| 529 | 538 | --
|
| 530 | 539 | -- test(bits64 buffer)
|
| ... | ... | @@ -543,7 +552,7 @@ opRegWidth w = pprPanic "opRegWidth" (text "Unsupported width" <+> ppr w) |
| 543 | 552 | -- Next we compute `c`: The `%not` requires no extension of its operands, but
|
| 544 | 553 | -- we must still truncate the result back down to 8-bits. Finally the `%shrl`
|
| 545 | 554 | -- requires no extension and no truncate since we can assume that
|
| 546 | --- `c` is zero-extended.
|
|
| 555 | +-- `c` is zero-extended (it was produced by a truncateReg in the same block).
|
|
| 547 | 556 | --
|
| 548 | 557 | -- TODO:
|
| 549 | 558 | -- Don't use Width in Operands
|
| ... | ... | @@ -877,17 +886,30 @@ getRegister' config plat expr |
| 877 | 886 | CmmMachOp (MO_U_Quot w) [x, y] | w == W8 -> do
|
| 878 | 887 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 879 | 888 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 880 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 881 | - (UXTB (OpReg w reg_y) (OpReg w reg_y)) `snocOL`
|
|
| 882 | - (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 889 | + tmp_x <- getNewRegNat (intFormat w)
|
|
| 890 | + tmp_y <- getNewRegNat (intFormat w)
|
|
| 891 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
| 892 | + (UXTB (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
| 893 | + (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
| 883 | 894 | CmmMachOp (MO_U_Quot w) [x, y] | w == W16 -> do
|
| 884 | 895 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 885 | 896 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 886 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 887 | - (UXTH (OpReg w reg_y) (OpReg w reg_y)) `snocOL`
|
|
| 888 | - (UDIV (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 897 | + tmp_x <- getNewRegNat (intFormat w)
|
|
| 898 | + tmp_y <- getNewRegNat (intFormat w)
|
|
| 899 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w tmp_x) (OpReg w reg_x)) `snocOL`
|
|
| 900 | + (UXTH (OpReg w tmp_y) (OpReg w reg_y)) `snocOL`
|
|
| 901 | + (UDIV (OpReg w dst) (OpReg w tmp_x) (OpReg w tmp_y)))
|
|
| 889 | 902 | |
| 890 | 903 | -- 2. Shifts. x << n, x >> n.
|
| 904 | + -- Sub-word left shifts by a constant: use UBFM (UBFIZ alias) to shift
|
|
| 905 | + -- and mask in a single instruction. See Note [Signed arithmetic on AArch64].
|
|
| 906 | + CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W8, 0 <= n, n < 8 -> do
|
|
| 907 | + (reg_x, _format_x, code_x) <- getSomeReg x
|
|
| 908 | + 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)))))
|
|
| 909 | + CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
|
| 910 | + (reg_x, _format_x, code_x) <- getSomeReg x
|
|
| 911 | + 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)))))
|
|
| 912 | + |
|
| 891 | 913 | CmmMachOp (MO_Shl w) [x, (CmmLit (CmmInt n _))]
|
| 892 | 914 | | w == W32 || w == W64
|
| 893 | 915 | , 0 <= n, n < fromIntegral (widthInBits w) -> do
|
| ... | ... | @@ -901,8 +923,11 @@ getRegister' config plat expr |
| 901 | 923 | CmmMachOp (MO_S_Shr w) [x, y] | w == W8 -> do
|
| 902 | 924 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 903 | 925 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 904 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 905 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)) `snocOL`
|
|
| 926 | + -- Use a temporary register to avoid sign-extending reg_x in-place,
|
|
| 927 | + -- as other operations may use reg_x.
|
|
| 928 | + tmp <- getNewRegNat (intFormat w)
|
|
| 929 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTB (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
| 930 | + (ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
| 906 | 931 | (UXTB (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
| 907 | 932 | |
| 908 | 933 | CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
| ... | ... | @@ -912,8 +937,11 @@ getRegister' config plat expr |
| 912 | 937 | CmmMachOp (MO_S_Shr w) [x, y] | w == W16 -> do
|
| 913 | 938 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 914 | 939 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 915 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 916 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)) `snocOL`
|
|
| 940 | + -- Use a temporary register to avoid sign-extending reg_x in-place,
|
|
| 941 | + -- as other operations may use reg_x.
|
|
| 942 | + tmp <- getNewRegNat (intFormat w)
|
|
| 943 | + return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (SXTH (OpReg w tmp) (OpReg w reg_x)) `snocOL`
|
|
| 944 | + (ASR (OpReg w dst) (OpReg w tmp) (OpReg w reg_y)) `snocOL`
|
|
| 917 | 945 | (UXTH (OpReg w dst) (OpReg w dst))) -- See Note [Signed arithmetic on AArch64]
|
| 918 | 946 | |
| 919 | 947 | CmmMachOp (MO_S_Shr w) [x, (CmmLit (CmmInt n _))]
|
| ... | ... | @@ -928,8 +956,8 @@ getRegister' config plat expr |
| 928 | 956 | CmmMachOp (MO_U_Shr w) [x, y] | w == W8 -> do
|
| 929 | 957 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 930 | 958 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 931 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTB (OpReg w reg_x) (OpReg w reg_x)) `snocOL`
|
|
| 932 | - (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 959 | + tmp <- getNewRegNat (intFormat w)
|
|
| 960 | + 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)))
|
|
| 933 | 961 | |
| 934 | 962 | CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))] | w == W16, 0 <= n, n < 16 -> do
|
| 935 | 963 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| ... | ... | @@ -937,8 +965,8 @@ getRegister' config plat expr |
| 937 | 965 | CmmMachOp (MO_U_Shr w) [x, y] | w == W16 -> do
|
| 938 | 966 | (reg_x, _format_x, code_x) <- getSomeReg x
|
| 939 | 967 | (reg_y, _format_y, code_y) <- getSomeReg y
|
| 940 | - return $ Any (intFormat w) (\dst -> code_x `appOL` code_y `snocOL` annExpr expr (UXTH (OpReg w reg_x) (OpReg w reg_x))
|
|
| 941 | - `snocOL` (ASR (OpReg w dst) (OpReg w reg_x) (OpReg w reg_y)))
|
|
| 968 | + tmp <- getNewRegNat (intFormat w)
|
|
| 969 | + 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)))
|
|
| 942 | 970 | |
| 943 | 971 | CmmMachOp (MO_U_Shr w) [x, (CmmLit (CmmInt n _))]
|
| 944 | 972 | | 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)) |
| ... | ... | @@ -12,3 +12,11 @@ test('bytearray-memcpy-unroll', is_amd64_codegen, compile_grep_asm, ['hs', True, |
| 12 | 12 | test('T18137', [when(opsys('darwin'), skip), only_ways(llvm_ways)], compile_grep_asm, ['hs', False, '-fllvm -split-sections'])
|
| 13 | 13 | |
| 14 | 14 | test('T24941', [only_ways(['optasm'])], compile, ['-fregs-graph'])
|
| 15 | +is_aarch64_codegen = [
|
|
| 16 | + unless(arch('aarch64'), skip),
|
|
| 17 | + when(unregisterised(), skip),
|
|
| 18 | +]
|
|
| 19 | + |
|
| 20 | +# AArch64-specific tests
|
|
| 21 | +test('aarch64-ushr-subword', is_aarch64_codegen, compile_grep_asm, ['hs', True, '-O'])
|
|
| 22 | +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 |
| ... | ... | @@ -275,3 +275,6 @@ test('T27072d', [req_c, only_ways(['dyn']), when(not opsys('darwin'), skip), |
| 275 | 275 | # Skipped on Darwin (Apple linker doesn't support --wrap).
|
| 276 | 276 | test('T27072w', [req_c, js_skip, when(opsys('darwin'), skip)],
|
| 277 | 277 | compile_and_run, ['T27072w_c.c -no-hs-main -optl-Wl,--wrap=hs_spt_remove'])
|
| 278 | +# AArch64-specific runtime tests
|
|
| 279 | +test('aarch64-ushr-subword-run', [unless(arch('aarch64'), skip)], compile_and_run, ['-O'])
|
|
| 280 | +test('aarch64-subword-ops', [unless(arch('aarch64'), skip)], compile_and_run, ['-O']) |