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
|