| ... |
... |
@@ -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
|
|