recursion-ninja pushed to branch wip/fix-26109 at Glasgow Haskell Compiler / GHC
Commits:
-
a522e725
by Recursion Ninja at 2025-08-08T13:33:17-04:00
-
3a9ec9d9
by Recursion Ninja at 2025-08-08T16:49:26-04:00
7 changed files:
- compiler/GHC/Builtin/primops.txt.pp
- testsuite/tests/numeric/should_run/foundation.hs
- utils/genprimopcode/Lexer.x
- utils/genprimopcode/Main.hs
- utils/genprimopcode/Parser.y
- utils/genprimopcode/ParserM.hs
- utils/genprimopcode/Syntax.hs
Changes:
| ... | ... | @@ -148,6 +148,7 @@ defaults |
| 148 | 148 | vector = []
|
| 149 | 149 | deprecated_msg = {} -- A non-empty message indicates deprecation
|
| 150 | 150 | div_like = False -- Second argument expected to be non zero - used for tests
|
| 151 | + defined_bits = Nothing -- The number of bits the operation is defined for (if not all bits)
|
|
| 151 | 152 | |
| 152 | 153 | -- Note [When do out-of-line primops go in primops.txt.pp]
|
| 153 | 154 | -- ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... | ... | @@ -1065,8 +1066,10 @@ primop CtzOp "ctz#" GenPrimOp Word# -> Word# |
| 1065 | 1066 | |
| 1066 | 1067 | primop BSwap16Op "byteSwap16#" GenPrimOp Word# -> Word#
|
| 1067 | 1068 | {Swap bytes in the lower 16 bits of a word. The higher bytes are undefined. }
|
| 1069 | + with defined_bits = 16
|
|
| 1068 | 1070 | primop BSwap32Op "byteSwap32#" GenPrimOp Word# -> Word#
|
| 1069 | 1071 | {Swap bytes in the lower 32 bits of a word. The higher bytes are undefined. }
|
| 1072 | + with defined_bits = 32
|
|
| 1070 | 1073 | primop BSwap64Op "byteSwap64#" GenPrimOp Word64# -> Word64#
|
| 1071 | 1074 | {Swap bytes in a 64 bits of a word.}
|
| 1072 | 1075 | primop BSwapOp "byteSwap#" GenPrimOp Word# -> Word#
|
| ... | ... | @@ -1074,10 +1077,13 @@ primop BSwapOp "byteSwap#" GenPrimOp Word# -> Word# |
| 1074 | 1077 | |
| 1075 | 1078 | primop BRev8Op "bitReverse8#" GenPrimOp Word# -> Word#
|
| 1076 | 1079 | {Reverse the order of the bits in a 8-bit word.}
|
| 1080 | + with defined_bits = 8
|
|
| 1077 | 1081 | primop BRev16Op "bitReverse16#" GenPrimOp Word# -> Word#
|
| 1078 | 1082 | {Reverse the order of the bits in a 16-bit word.}
|
| 1083 | + with defined_bits = 16
|
|
| 1079 | 1084 | primop BRev32Op "bitReverse32#" GenPrimOp Word# -> Word#
|
| 1080 | 1085 | {Reverse the order of the bits in a 32-bit word.}
|
| 1086 | + with defined_bits = 32
|
|
| 1081 | 1087 | primop BRev64Op "bitReverse64#" GenPrimOp Word64# -> Word64#
|
| 1082 | 1088 | {Reverse the order of the bits in a 64-bit word.}
|
| 1083 | 1089 | primop BRevOp "bitReverse#" GenPrimOp Word# -> Word#
|
| ... | ... | @@ -409,6 +409,33 @@ instance TestPrimop (Word# -> Int# -> Word#) where |
| 409 | 409 | testPrimop s l r = Property s $ \(uWord -> a1) (uInt -> a2) -> (wWord (l a1 a2)) === wWord (r a1 a2)
|
| 410 | 410 | -}
|
| 411 | 411 | |
| 412 | +-- | A special data-type for representing functions where,
|
|
| 413 | +-- since only some number of the lower bits are defined,
|
|
| 414 | +-- testing for strict equality in the undefined upper bits is not appropriate!
|
|
| 415 | +-- Without using this data-type, false-positive failures will be reported
|
|
| 416 | +-- when the undefined bit regions do not match, even though the equality of bits
|
|
| 417 | +-- in this undefined region has no bearing on correctness.
|
|
| 418 | +data LowerBitsAreDefined =
|
|
| 419 | + LowerBitsAreDefined
|
|
| 420 | + { definedLowerWidth :: Word
|
|
| 421 | + -- ^ The (strictly-non-negative) number of least-significant bits
|
|
| 422 | + -- for which the attached function is defined.
|
|
| 423 | + , undefinedBehavior :: (Word# -> Word#)
|
|
| 424 | + -- ^ Function with undefined behavior for some of its most significant bits.
|
|
| 425 | + }
|
|
| 426 | + |
|
| 427 | +instance TestPrimop LowerBitsAreDefined where
|
|
| 428 | + testPrimop s l r = Property s $ \ (uWord#-> x0) ->
|
|
| 429 | + let -- Create a mask to unset all bits in the undefined area,
|
|
| 430 | + -- leaving set bits only in the area of defined behavior.
|
|
| 431 | + -- Since the upper bits are undefined,
|
|
| 432 | + -- if the function defines behavior for the lower N bits,
|
|
| 433 | + -- then /only/ the lower N bits are preserved,
|
|
| 434 | + -- and the upper WORDSIZE - N bits are discarded.
|
|
| 435 | + mask = bit (fromEnum (definedLowerWidth r)) - 1
|
|
| 436 | + valL = wWord# (undefinedBehavior l x0) .&. mask
|
|
| 437 | + valR = wWord# (undefinedBehavior r x0) .&. mask
|
|
| 438 | + in valL === valR
|
|
| 412 | 439 | |
| 413 | 440 | twoNonZero :: (a -> a -> b) -> a -> NonZero a -> b
|
| 414 | 441 | twoNonZero f x (NonZero y) = f x y
|
| ... | ... | @@ -673,34 +700,6 @@ testPrimops = Group "primop" |
| 673 | 700 | , testPrimop "narrow32Word#" Primop.narrow32Word# Wrapper.narrow32Word#
|
| 674 | 701 | ]
|
| 675 | 702 | |
| 676 | --- | A special data-type for representing functions where,
|
|
| 677 | --- since only some number of the lower bits are defined,
|
|
| 678 | --- testing for strict equality in the undefined upper bits is not appropriate!
|
|
| 679 | --- Without using this data-type, false-positive failures will be reported
|
|
| 680 | --- when the undefined bit regions do not match, even though the equality of bits
|
|
| 681 | --- in this undefined region has no bearing on correctness.
|
|
| 682 | -data LowerBitsAreDefined =
|
|
| 683 | - LowerBitsAreDefined
|
|
| 684 | - { definedLowerWidth :: Word
|
|
| 685 | - -- ^ The (strictly-non-negative) number of least-significant bits
|
|
| 686 | - -- for which the attached function is defined.
|
|
| 687 | - , undefinedBehavior :: (Word# -> Word#)
|
|
| 688 | - -- ^ Function with undefined behavior for some of its most significant bits.
|
|
| 689 | - }
|
|
| 690 | - |
|
| 691 | -instance TestPrimop LowerBitsAreDefined where
|
|
| 692 | - testPrimop s l r = Property s $ \ (uWord#-> x0) ->
|
|
| 693 | - let -- Create a mask to unset all bits in the undefined area,
|
|
| 694 | - -- leaving set bits only in the area of defined behavior.
|
|
| 695 | - -- Since the upper bits are undefined,
|
|
| 696 | - -- if the function defines behavior for the lower N bits,
|
|
| 697 | - -- then /only/ the lower N bits are preserved,
|
|
| 698 | - -- and the upper WORDSIZE - N bits are discarded.
|
|
| 699 | - mask = bit (fromEnum (definedLowerWidth r)) - 1
|
|
| 700 | - valL = wWord# (undefinedBehavior l x0) .&. mask
|
|
| 701 | - valR = wWord# (undefinedBehavior r x0) .&. mask
|
|
| 702 | - in valL === valR
|
|
| 703 | - |
|
| 704 | 703 | instance TestPrimop (Char# -> Char# -> Int#) where
|
| 705 | 704 | testPrimop s l r = Property s $ \ (uChar#-> x0) (uChar#-> x1) -> wInt# (l x0 x1) === wInt# (r x0 x1)
|
| 706 | 705 |
| ... | ... | @@ -56,6 +56,7 @@ words :- |
| 56 | 56 | <0> "CanFail" { mkT TCanFail }
|
| 57 | 57 | <0> "ThrowsException" { mkT TThrowsException }
|
| 58 | 58 | <0> "ReadWriteEffect" { mkT TReadWriteEffect }
|
| 59 | + <0> "defined_bits" { mkT TDefinedBits }
|
|
| 59 | 60 | <0> "can_fail_warning" { mkT TCanFailWarnFlag }
|
| 60 | 61 | <0> "DoNotWarnCanFail" { mkT TDoNotWarnCanFail }
|
| 61 | 62 | <0> "WarnIfEffectIsCanFail" { mkT TWarnIfEffectIsCanFail }
|
| ... | ... | @@ -10,6 +10,7 @@ module Main where |
| 10 | 10 | import Parser
|
| 11 | 11 | import Syntax
|
| 12 | 12 | |
| 13 | +import Control.Applicative (asum)
|
|
| 13 | 14 | import Data.Char
|
| 14 | 15 | import Data.List (union, intersperse, intercalate, nub, sort)
|
| 15 | 16 | import Data.Maybe ( catMaybes, mapMaybe )
|
| ... | ... | @@ -753,7 +754,14 @@ gen_foundation_tests (Info _ entries) |
| 753 | 754 | = let testPrimOpHow = if is_divLikeOp po
|
| 754 | 755 | then "testPrimopDivLike"
|
| 755 | 756 | else "testPrimop"
|
| 756 | - in Just $ intercalate " " [testPrimOpHow, "\"" ++ poName ++ "\"", wrap "Primop" poName, wrap "Wrapper" poName]
|
|
| 757 | + withDefinedBits qName = case mb_defined_bits po of
|
|
| 758 | + Nothing -> qName
|
|
| 759 | + Just bs -> concat ["(", show bs, " `LowerBitsAreDefined` ", qName, ")"]
|
|
| 760 | + in Just $ intercalate " "
|
|
| 761 | + [ testPrimOpHow
|
|
| 762 | + , "\"" ++ poName ++ "\""
|
|
| 763 | + , withDefinedBits $ wrap "Primop" poName
|
|
| 764 | + , withDefinedBits $ wrap "Wrapper" poName]
|
|
| 757 | 765 | | otherwise = Nothing
|
| 758 | 766 | |
| 759 | 767 | |
| ... | ... | @@ -771,6 +779,17 @@ gen_foundation_tests (Info _ entries) |
| 771 | 779 | divableTyCons = ["Int#", "Word#", "Word8#", "Word16#", "Word32#", "Word64#"
|
| 772 | 780 | ,"Int8#", "Int16#", "Int32#", "Int64#"]
|
| 773 | 781 | |
| 782 | + |
|
| 783 | + mb_defined_bits :: Entry -> Maybe Word
|
|
| 784 | + mb_defined_bits op@(PrimOpSpec{}) =
|
|
| 785 | + let opOpts = opts op
|
|
| 786 | + getDefBits :: Option -> Maybe Word
|
|
| 787 | + getDefBits (OptionDefinedBits x) = x
|
|
| 788 | + getDefBits _ = Nothing
|
|
| 789 | + in asum $ getDefBits <$> opOpts
|
|
| 790 | + mb_defined_bits _ = Nothing
|
|
| 791 | + |
|
| 792 | + |
|
| 774 | 793 | ------------------------------------------------------------------
|
| 775 | 794 | -- Create PrimOpInfo text from PrimOpSpecs -----------------------
|
| 776 | 795 | ------------------------------------------------------------------
|
| ... | ... | @@ -50,6 +50,7 @@ import AccessOps |
| 50 | 50 | CanFail { TCanFail }
|
| 51 | 51 | ThrowsException { TThrowsException }
|
| 52 | 52 | ReadWriteEffect { TReadWriteEffect }
|
| 53 | + defined_bits { TDefinedBits }
|
|
| 53 | 54 | can_fail_warning { TCanFailWarnFlag }
|
| 54 | 55 | DoNotWarnCanFail { TDoNotWarnCanFail }
|
| 55 | 56 | WarnIfEffectIsCanFail { TWarnIfEffectIsCanFail }
|
| ... | ... | @@ -81,13 +82,14 @@ pOptions : pOption pOptions { $1 : $2 } |
| 81 | 82 | | {- empty -} { [] }
|
| 82 | 83 | |
| 83 | 84 | pOption :: { Option }
|
| 84 | -pOption : lowerName '=' false { OptionFalse $1 }
|
|
| 85 | - | lowerName '=' true { OptionTrue $1 }
|
|
| 86 | - | lowerName '=' pStuffBetweenBraces { OptionString $1 $3 }
|
|
| 87 | - | lowerName '=' integer { OptionInteger $1 $3 }
|
|
| 88 | - | vector '=' pVectorTemplate { OptionVector $3 }
|
|
| 89 | - | fixity '=' pInfix { OptionFixity $3 }
|
|
| 90 | - | effect '=' pEffect { OptionEffect $3 }
|
|
| 85 | +pOption : lowerName '=' false { OptionFalse $1 }
|
|
| 86 | + | lowerName '=' true { OptionTrue $1 }
|
|
| 87 | + | lowerName '=' pStuffBetweenBraces { OptionString $1 $3 }
|
|
| 88 | + | lowerName '=' integer { OptionInteger $1 $3 }
|
|
| 89 | + | vector '=' pVectorTemplate { OptionVector $3 }
|
|
| 90 | + | fixity '=' pInfix { OptionFixity $3 }
|
|
| 91 | + | effect '=' pEffect { OptionEffect $3 }
|
|
| 92 | + | defined_bits '=' pGoodBits { OptionDefinedBits $3 }
|
|
| 91 | 93 | | can_fail_warning '=' pPrimOpCanFailWarnFlag { OptionCanFailWarnFlag $3 }
|
| 92 | 94 | |
| 93 | 95 | pInfix :: { Maybe Fixity }
|
| ... | ... | @@ -102,6 +104,10 @@ pEffect : NoEffect { NoEffect } |
| 102 | 104 | | ThrowsException { ThrowsException }
|
| 103 | 105 | | ReadWriteEffect { ReadWriteEffect }
|
| 104 | 106 | |
| 107 | +pGoodBits :: { Maybe Word }
|
|
| 108 | +pGoodBits : integer { Just $ toEnum $1 }
|
|
| 109 | + | nothing { Nothing }
|
|
| 110 | + |
|
| 105 | 111 | pPrimOpCanFailWarnFlag :: { PrimOpCanFailWarnFlag }
|
| 106 | 112 | pPrimOpCanFailWarnFlag : DoNotWarnCanFail { DoNotWarnCanFail }
|
| 107 | 113 | | WarnIfEffectIsCanFail { WarnIfEffectIsCanFail }
|
| ... | ... | @@ -116,6 +116,7 @@ data Token = TEOF |
| 116 | 116 | | TCanFail
|
| 117 | 117 | | TThrowsException
|
| 118 | 118 | | TReadWriteEffect
|
| 119 | + | TDefinedBits
|
|
| 119 | 120 | | TCanFailWarnFlag
|
| 120 | 121 | | TDoNotWarnCanFail
|
| 121 | 122 | | TWarnIfEffectIsCanFail
|
| ... | ... | @@ -76,6 +76,7 @@ data Option |
| 76 | 76 | | OptionFixity (Maybe Fixity) -- fixity = infix{,l,r} <int> | Nothing
|
| 77 | 77 | | OptionEffect PrimOpEffect -- effect = NoEffect | DoNotSpeculate | CanFail | ThrowsException | ReadWriteEffect | FallibleReadWriteEffect
|
| 78 | 78 | | OptionCanFailWarnFlag PrimOpCanFailWarnFlag -- can_fail_warning = DoNotWarnCanFail | WarnIfEffectIsCanFail | YesWarnCanFail
|
| 79 | + | OptionDefinedBits (Maybe Word) -- defined_bits = Just 16 | Nothing
|
|
| 79 | 80 | deriving Show
|
| 80 | 81 | |
| 81 | 82 | -- categorises primops
|
| ... | ... | @@ -196,6 +197,7 @@ get_attrib_name (OptionVector _) = "vector" |
| 196 | 197 | get_attrib_name (OptionFixity _) = "fixity"
|
| 197 | 198 | get_attrib_name (OptionEffect _) = "effect"
|
| 198 | 199 | get_attrib_name (OptionCanFailWarnFlag _) = "can_fail_warning"
|
| 200 | +get_attrib_name (OptionDefinedBits _) = "defined_bits"
|
|
| 199 | 201 | |
| 200 | 202 | lookup_attrib :: String -> [Option] -> Maybe Option
|
| 201 | 203 | lookup_attrib _ [] = Nothing
|