Simon Jakobi pushed to branch wip/sjakobi/T27619-liveness-fixpoint-formats at Glasgow Haskell Compiler / GHC
Commits:
-
1ad7425a
by Simon Jakobi at 2026-08-17T23:12:15+02:00
4 changed files:
- compiler/GHC/CmmToAsm/Format.hs
- compiler/GHC/CmmToAsm/PPC/CodeGen.hs
- compiler/GHC/CmmToAsm/Reg/Liveness.hs
- compiler/GHC/CmmToAsm/Reg/Regs.hs
Changes:
| ... | ... | @@ -23,6 +23,7 @@ module GHC.CmmToAsm.Format ( |
| 23 | 23 | vecFormat,
|
| 24 | 24 | isVecFormat,
|
| 25 | 25 | cmmTypeFormat,
|
| 26 | + compareFormat,
|
|
| 26 | 27 | formatToWidth,
|
| 27 | 28 | scalarWidth,
|
| 28 | 29 | formatInBytes,
|
| ... | ... | @@ -40,11 +41,13 @@ where |
| 40 | 41 | |
| 41 | 42 | import GHC.Prelude
|
| 42 | 43 | |
| 44 | +import Data.Semigroup ( (<>) )
|
|
| 45 | + |
|
| 43 | 46 | import GHC.Cmm
|
| 44 | 47 | import GHC.Platform.Reg ( Reg(..), RealReg, VirtualReg )
|
| 45 | 48 | import GHC.Types.Unique ( Uniquable(..) )
|
| 46 | 49 | import GHC.Types.Unique.Set
|
| 47 | -import GHC.Utils.Outputable
|
|
| 50 | +import GHC.Utils.Outputable hiding ( (<>) )
|
|
| 48 | 51 | import GHC.Utils.Panic
|
| 49 | 52 | |
| 50 | 53 | {- Note [GHC's data format representations]
|
| ... | ... | @@ -92,7 +95,9 @@ data Format |
| 92 | 95 | | FF64
|
| 93 | 96 | | VecFormat !Length -- ^ number of elements (always at least 2)
|
| 94 | 97 | !ScalarFormat -- ^ format of each element
|
| 95 | - deriving (Show, Eq, Ord)
|
|
| 98 | + deriving (Show, Eq)
|
|
| 99 | + -- No Ord: compare via 'formatToWidth', or use 'compareFormat' where a
|
|
| 100 | + -- total order is needed.
|
|
| 96 | 101 | |
| 97 | 102 | pattern IntegerFormat :: Format
|
| 98 | 103 | pattern IntegerFormat <- ( isIntegerFormat -> True )
|
| ... | ... | @@ -117,7 +122,7 @@ data ScalarFormat |
| 117 | 122 | | FmtInt64
|
| 118 | 123 | | FmtFloat
|
| 119 | 124 | | FmtDouble
|
| 120 | - deriving (Show, Eq, Ord)
|
|
| 125 | + deriving (Show, Eq)
|
|
| 121 | 126 | |
| 122 | 127 | scalarFormatFormat :: ScalarFormat -> Format
|
| 123 | 128 | scalarFormatFormat = \case
|
| ... | ... | @@ -248,6 +253,33 @@ scalarWidth = \case |
| 248 | 253 | formatInBytes :: Format -> Int
|
| 249 | 254 | formatInBytes = widthInBytes . formatToWidth
|
| 250 | 255 | |
| 256 | +-- | Total order on formats: by width, with an arbitrary but fixed tiebreak
|
|
| 257 | +-- between distinct formats of the same width.
|
|
| 258 | +--
|
|
| 259 | +-- See Note [Convergence of the liveness fixpoint] in GHC.CmmToAsm.Reg.Liveness.
|
|
| 260 | +compareFormat :: Format -> Format -> Ordering
|
|
| 261 | +compareFormat f1 f2 =
|
|
| 262 | + compare (formatToWidth f1) (formatToWidth f2) <> compare (tag f1) (tag f2)
|
|
| 263 | + where
|
|
| 264 | + tag :: Format -> (Int, Length)
|
|
| 265 | + tag = \case
|
|
| 266 | + II8 -> (0, 0)
|
|
| 267 | + II16 -> (1, 0)
|
|
| 268 | + II32 -> (2, 0)
|
|
| 269 | + II64 -> (3, 0)
|
|
| 270 | + FF32 -> (4, 0)
|
|
| 271 | + FF64 -> (5, 0)
|
|
| 272 | + VecFormat l s -> (6 + scalarTag s, l)
|
|
| 273 | + |
|
| 274 | + scalarTag :: ScalarFormat -> Int
|
|
| 275 | + scalarTag = \case
|
|
| 276 | + FmtInt8 -> 0
|
|
| 277 | + FmtInt16 -> 1
|
|
| 278 | + FmtInt32 -> 2
|
|
| 279 | + FmtInt64 -> 3
|
|
| 280 | + FmtFloat -> 4
|
|
| 281 | + FmtDouble -> 5
|
|
| 282 | + |
|
| 251 | 283 | --------------------------------------------------------------------------------
|
| 252 | 284 | |
| 253 | 285 | -- | A typed virtual register: a virtual register, together with the specific
|
| ... | ... | @@ -484,7 +484,7 @@ getRegister' _ _ (CmmMachOp (MO_SS_Conv src tgt) [CmmLoad mem pk _]) |
| 484 | 484 | , src < tgt = do
|
| 485 | 485 | let format = cmmTypeFormat pk
|
| 486 | 486 | -- lwa is DS-form. See Note [Power instruction format]
|
| 487 | - let form = if format >= II32 then DS else D
|
|
| 487 | + let form = if formatToWidth format >= W32 then DS else D
|
|
| 488 | 488 | Amode addr addr_code <- getAmode form mem
|
| 489 | 489 | let code dst = assert (format == intFormat src)
|
| 490 | 490 | $ addr_code `snocOL` LA format dst addr
|
| ... | ... | @@ -937,9 +937,9 @@ test: |
| 937 | 937 | dependency order. Comparing the whole accumulated block map would make the
|
| 938 | 938 | fixpoint quadratic in procedure size (#27437).
|
| 939 | 939 | |
| 940 | -The fixpoint terminates because the entries can only grow: registers are
|
|
| 941 | -only added, formats only increase via 'maxRegWithFormat' joins, and both
|
|
| 942 | -lattices are finite.
|
|
| 940 | +To ensure termination, 'maxRegWithFormat' uses 'compareFormat' which defines a
|
|
| 941 | +total order on formats. Comparing the widths only would introduce the risk of
|
|
| 942 | +an infinite loop where each iteration swaps two formats of the same width.
|
|
| 943 | 943 | -}
|
| 944 | 944 | |
| 945 | 945 |
| ... | ... | @@ -20,7 +20,8 @@ module GHC.CmmToAsm.Reg.Regs ( |
| 20 | 20 | import GHC.Prelude
|
| 21 | 21 | |
| 22 | 22 | import GHC.Platform.Reg ( Reg )
|
| 23 | -import GHC.CmmToAsm.Format ( Format, RegWithFormat(..), isVecFormat )
|
|
| 23 | +import GHC.CmmToAsm.Format ( Format, RegWithFormat(..), isVecFormat,
|
|
| 24 | + compareFormat, formatToWidth )
|
|
| 24 | 25 | |
| 25 | 26 | import GHC.Utils.Outputable ( Outputable )
|
| 26 | 27 | import GHC.Types.Unique ( Uniquable(..) )
|
| ... | ... | @@ -39,11 +40,12 @@ newtype Regs = Regs { getRegs :: UniqSet RegWithFormat } |
| 39 | 40 | |
| 40 | 41 | maxRegWithFormat :: RegWithFormat -> RegWithFormat -> RegWithFormat
|
| 41 | 42 | maxRegWithFormat r1@(RegWithFormat _ fmt1) r2@(RegWithFormat _ fmt2)
|
| 42 | - = if fmt1 >= fmt2
|
|
| 43 | - then r1
|
|
| 44 | - else r2
|
|
| 45 | - -- Re-using one of the arguments avoids allocating a new 'RegWithFormat',
|
|
| 46 | - -- compared with returning 'RegWithFormat r1 (max fmt1 fmt2)'.
|
|
| 43 | + | LT <- compareFormat fmt1 fmt2 = r2
|
|
| 44 | + | otherwise = r1
|
|
| 45 | + -- See Note [Convergence of the liveness fixpoint] in
|
|
| 46 | + -- GHC.CmmToAsm.Reg.Liveness.
|
|
| 47 | + --
|
|
| 48 | + -- Re-using one of the arguments avoids allocating a new 'RegWithFormat'.
|
|
| 47 | 49 | |
| 48 | 50 | noRegs :: Regs
|
| 49 | 51 | noRegs = Regs emptyUniqSet
|
| ... | ... | @@ -66,7 +68,7 @@ minusCoveredRegs = coerce $ minusUniqSet_C f |
| 66 | 68 | where
|
| 67 | 69 | f :: RegWithFormat -> RegWithFormat -> Maybe RegWithFormat
|
| 68 | 70 | f r1@(RegWithFormat _ fmt1) (RegWithFormat _ fmt2) =
|
| 69 | - if fmt2 >= fmt1
|
|
| 71 | + if formatToWidth fmt2 >= formatToWidth fmt1
|
|
| 70 | 72 | ||
|
| 71 | 73 | not ( isVecFormat fmt1 )
|
| 72 | 74 | -- See Wrinkle [Don't allow scalar partial writes]
|
| ... | ... | @@ -99,7 +101,7 @@ shrinkingRegs = coerce $ minusUniqSet_C f |
| 99 | 101 | where
|
| 100 | 102 | f :: RegWithFormat -> RegWithFormat -> Maybe RegWithFormat
|
| 101 | 103 | f (RegWithFormat _ fmt1) r2@(RegWithFormat _ fmt2)
|
| 102 | - | fmt2 < fmt1
|
|
| 104 | + | formatToWidth fmt2 < formatToWidth fmt1
|
|
| 103 | 105 | = Just r2
|
| 104 | 106 | | otherwise
|
| 105 | 107 | = Nothing
|