Simon Jakobi pushed to branch wip/sjakobi/udfm-placement at Glasgow Haskell Compiler / GHC

Commits:

1 changed file:

Changes:

  • compiler/GHC/Types/Unique/DFM.hs
    ... ... @@ -74,7 +74,6 @@ import GHC.Prelude
    74 74
     
    
    75 75
     import GHC.Types.Unique ( Uniquable(..), Unique, getKey, mkUniqueGrimily )
    
    76 76
     import GHC.Utils.Outputable
    
    77
    -import GHC.Utils.Panic (panic)
    
    78 77
     
    
    79 78
     import qualified GHC.Data.Word64Map.Strict as MS
    
    80 79
     import qualified GHC.Data.Word64Map as M
    
    ... ... @@ -136,24 +135,19 @@ import qualified GHC.Data.Word64Set as W
    136 135
     
    
    137 136
     -- | A type of values carrying an insertion tag
    
    138 137
     data TaggedVal val =
    
    139
    -    TaggedVal
    
    140
    -      !val
    
    141
    -      {-# UNPACK #-} !Int -- ^ insertion tag
    
    142
    -  | TaggedHole            -- ^ placement-sort gap sentinel; never stored in a map.
    
    143
    -                          --   See Note [Sorting a UDFM].
    
    138
    +  TaggedVal
    
    139
    +    !val
    
    140
    +    {-# UNPACK #-} !Int -- ^ insertion tag
    
    144 141
       deriving stock (Data, Functor, Foldable, Traversable)
    
    145 142
     
    
    146 143
     taggedFst :: TaggedVal val -> val
    
    147 144
     taggedFst (TaggedVal v _) = v
    
    148
    -taggedFst TaggedHole      = panic "taggedFst: TaggedHole"
    
    149 145
     
    
    150 146
     taggedSnd :: TaggedVal val -> Int
    
    151 147
     taggedSnd (TaggedVal _ i) = i
    
    152
    -taggedSnd TaggedHole      = panic "taggedSnd: TaggedHole"
    
    153 148
     
    
    154 149
     instance Eq val => Eq (TaggedVal val) where
    
    155 150
       (TaggedVal v1 _) == (TaggedVal v2 _) = v1 == v2
    
    156
    -  _                == _                = panic "TaggedVal (==): TaggedHole"
    
    157 151
     
    
    158 152
     -- | Type of unique deterministic finite maps
    
    159 153
     --
    
    ... ... @@ -213,7 +207,7 @@ addToUDFM_Directly :: UniqDFM key elt -> Unique -> elt -> UniqDFM key elt
    213 207
     addToUDFM_Directly (UDFM m i) u v
    
    214 208
       = UDFM (MS.insertWith tf (getKey u) (TaggedVal v i) m) (i + 1)
    
    215 209
       where
    
    216
    -    tf new old = TaggedVal (taggedFst new) (taggedSnd old)
    
    210
    +    tf (TaggedVal new_v _) (TaggedVal _ old_i) = TaggedVal new_v old_i
    
    217 211
           -- Keep the old tag, but insert the new value
    
    218 212
           -- This means that udfmToList typically returns elements
    
    219 213
           -- in the order of insertion, rather than the reverse
    
    ... ... @@ -230,8 +224,8 @@ addToUDFM_C_Directly
    230 224
     addToUDFM_C_Directly f (UDFM m i) u v
    
    231 225
       = UDFM (MS.insertWith tf (getKey u) (TaggedVal v i) m) (i + 1)
    
    232 226
         where
    
    233
    -      tf new old
    
    234
    -         = TaggedVal (f (taggedFst old) (taggedFst new)) (taggedSnd old)
    
    227
    +      tf (TaggedVal new_v _) (TaggedVal old_v old_i)
    
    228
    +         = TaggedVal (f old_v new_v) old_i
    
    235 229
               -- Flip the arguments, because M.insertWith uses  (new->old->result)
    
    236 230
               --                         but f            needs (old->new->result)
    
    237 231
               -- Like addToUDFM_Directly, keep the old tag
    
    ... ... @@ -384,7 +378,7 @@ foldWithKeyUDFM k z m = foldr (uncurry k) z (udfmToList m)
    384 378
     nonDetStrictFoldUDFM :: (elt -> a -> a) -> a -> UniqDFM key elt -> a
    
    385 379
     nonDetStrictFoldUDFM k z (UDFM m _i) = foldl' k' z m
    
    386 380
       where
    
    387
    -    k' acc tv = k (taggedFst tv) acc
    
    381
    +    k' acc (TaggedVal v _) = k v acc
    
    388 382
     
    
    389 383
     {- Note [Cost of deterministic iteration]
    
    390 384
     ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
    
    ... ... @@ -446,13 +440,38 @@ folding the sorted elements with no intermediate list. foldUDFM is INLINE and
    446 440
     dispatches through the out-of-line fold_elts_nonempty, where the fusion happens
    
    447 441
     once -- so the fold is list-free without inlining the sort into every consumer.
    
    448 442
     
    
    449
    -Holes: slots whose tag never occurs keep the initial sentinel, the nullary
    
    450
    -constructor TaggedHole. The readout distinguishes it from a real TaggedVal by
    
    451
    -constructor, so it never touches a filled element's value in a hole slot.
    
    452
    -TaggedHole is static (a nullary constructor is a single shared closure), so
    
    453
    -newSmallArray fills every slot with the same pointer -- no per-call allocation,
    
    454
    -nothing retained. TaggedHole must never be stored in a map; the accessors
    
    455
    -(taggedFst, taggedSnd) panic on it to catch any invariant violation.
    
    443
    +Holes: slots whose tag never occurs keep the initial sentinel, a TaggedVal
    
    444
    +with tag -1. Real tags are non-negative, so the readout skips slots with
    
    445
    +tag < 0 and takes the value field only from filled slots. That value field
    
    446
    +is strict, so the sentinel must hold something in WHNF even though it is
    
    447
    +never used: we use @unsafeCoerce () :: r@. This is safe because
    
    448
    +
    
    449
    +  (S1) r is a lifted boxed type (it is the map's element type), so a
    
    450
    +       pointer to the static () closure has the right representation and
    
    451
    +       the GC can trace it; and
    
    452
    +
    
    453
    +  (S2) the coerced value is never entered or otherwise used at type r:
    
    454
    +       the sentinel is the only TaggedVal with a negative tag, and the
    
    455
    +       readout takes values only from slots with tag >= 0.
    
    456
    +
    
    457
    +Since () is a single static closure, newSmallArray fills every slot with
    
    458
    +the same pointer: no per-call allocation, nothing retained.
    
    459
    +
    
    460
    +The sentinel stays local to placementSort: it exists only in the array
    
    461
    +between fill and readout, never in a map, so nothing outside this function
    
    462
    +needs to know about it. Alternative hole representations all cost more:
    
    463
    +
    
    464
    +  * a panic in the value field: forced (the field is strict) as soon as
    
    465
    +    the readout inspects the first hole's tag -- a crash, not a sentinel;
    
    466
    +  * borrowing a real element of the map: type-checks, but is a per-call
    
    467
    +    thunk that retains the whole source map until the holes are dropped;
    
    468
    +  * a dedicated hole constructor in TaggedVal: no coercion, but the
    
    469
    +    sentinel leaks into the type -- every match in this module must cover
    
    470
    +    a constructor that never occurs outside placementSort, and projecting
    
    471
    +    via partial accessors instead allocates a thunk wherever the value is
    
    472
    +    passed to an unknown function (T24471 ghc/alloc +3%);
    
    473
    +  * an extra sum wrapped around the array elements (e.g. Maybe): an
    
    474
    +    allocation per element in the fill.
    
    456 475
     
    
    457 476
     This sorting method loses when ub is much larger than n = M.size m: ub never
    
    458 477
     shrinks (overwrites keep bumping it, delete/filter shrink n but not ub). We
    
    ... ... @@ -484,10 +503,10 @@ placementSort :: forall e r. Int
    484 503
                                   -- consuming fold. See Note [Sorting a UDFM].
    
    485 504
     placementSort ub mk m = build gen
    
    486 505
       where
    
    487
    -    -- Unfilled slots hold the TaggedHole sentinel; the readout skips it.
    
    488
    -    -- See Note [Sorting a UDFM].
    
    506
    +    -- The tag -1 marks unfilled slots; the value field is never read, but it
    
    507
    +    -- is strict, so it needs a WHNF value of type r. See Note [Sorting a UDFM].
    
    489 508
         hole :: TaggedVal r
    
    490
    -    hole = TaggedHole
    
    509
    +    hole = TaggedVal (unsafeCoerce ()) (-1)
    
    491 510
     
    
    492 511
         fill :: SmallMutableArray s (TaggedVal r) -> State# s -> (# State# s, () #)
    
    493 512
         fill marr s = case M.traverseWithKey_ write m of ST st -> st s
    
    ... ... @@ -508,17 +527,17 @@ placementSort ub mk m = build gen
    508 527
             readout :: SmallArray (TaggedVal r) -> Int -> b
    
    509 528
             readout arr j
    
    510 529
               | j >= ub   = nil
    
    511
    -          | otherwise = case indexSmallArray arr j of
    
    512
    -              TaggedHole    -> readout arr (j + 1)
    
    513
    -              TaggedVal v _ -> cons v (readout arr (j + 1))
    
    530
    +          | t < 0     = readout arr (j + 1)
    
    531
    +          | otherwise = cons v (readout arr (j + 1))
    
    532
    +          where TaggedVal v t = indexSmallArray arr j
    
    514 533
     
    
    515 534
     filterUDFM :: (elt -> Bool) -> UniqDFM key elt -> UniqDFM key elt
    
    516
    -filterUDFM p (UDFM m i) = UDFM (M.filter (p . taggedFst) m) i
    
    535
    +filterUDFM p (UDFM m i) = UDFM (M.filter (\(TaggedVal v _) -> p v) m) i
    
    517 536
     
    
    518 537
     filterUDFM_Directly :: (Unique -> elt -> Bool) -> UniqDFM key elt -> UniqDFM key elt
    
    519 538
     filterUDFM_Directly p (UDFM m i) = UDFM (M.filterWithKey p' m) i
    
    520 539
       where
    
    521
    -  p' k tv = p (mkUniqueGrimily k) (taggedFst tv)
    
    540
    +  p' k (TaggedVal v _) = p (mkUniqueGrimily k) v
    
    522 541
     
    
    523 542
     udfmRestrictKeys :: UniqDFM key elt -> UniqDFM key elt2 -> UniqDFM key elt
    
    524 543
     udfmRestrictKeys (UDFM a i) (UDFM b _) = UDFM (M.restrictKeys a (M.keysSet b)) i
    
    ... ... @@ -642,7 +661,7 @@ alterUDFM f (UDFM m i) k =
    642 661
       UDFM (M.alter alterf (getKey $ getUnique k) m) (i + 1)
    
    643 662
       where
    
    644 663
       alterf Nothing = inject $ f Nothing
    
    645
    -  alterf (Just tv) = inject $ f (Just (taggedFst tv))
    
    664
    +  alterf (Just (TaggedVal v _)) = inject $ f (Just v)
    
    646 665
       inject Nothing = Nothing
    
    647 666
       inject (Just v) = Just $ TaggedVal v i
    
    648 667
     
    
    ... ... @@ -661,7 +680,7 @@ upsertUDFM f (UDFM m i) k =
    661 680
       UDFM (MS.upsert upsertf (getKey $ getUnique k) m) (i + 1)
    
    662 681
       where
    
    663 682
         upsertf Nothing = TaggedVal (f Nothing) i
    
    664
    -    upsertf (Just tv) = TaggedVal (f (Just (taggedFst tv))) i
    
    683
    +    upsertf (Just (TaggedVal v _)) = TaggedVal (f (Just v)) i
    
    665 684
     
    
    666 685
     -- | The expression (@'alterUDFM_L' f map k@) alters value @x@ at @k@, or absence
    
    667 686
     -- thereof and returns the new element at @k@ if there is any.
    
    ... ... @@ -684,7 +703,7 @@ alterUDFM_L f (UDFM m i) k =
    684 703
       where
    
    685 704
       alterf :: Maybe (TaggedVal elt) -> (Maybe (TaggedVal elt))
    
    686 705
       alterf Nothing = inject $ f Nothing
    
    687
    -  alterf (Just tv) = inject $ f (Just (taggedFst tv))
    
    706
    +  alterf (Just (TaggedVal v _)) = inject $ f (Just v)
    
    688 707
       inject Nothing = Nothing
    
    689 708
       inject (Just v) = Just $ TaggedVal v i
    
    690 709