| ... |
... |
@@ -74,6 +74,7 @@ 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)
|
|
77
|
78
|
|
|
78
|
79
|
import qualified GHC.Data.Word64Map.Strict as MS
|
|
79
|
80
|
import qualified GHC.Data.Word64Map as M
|
| ... |
... |
@@ -135,19 +136,24 @@ import qualified GHC.Data.Word64Set as W |
|
135
|
136
|
|
|
136
|
137
|
-- | A type of values carrying an insertion tag
|
|
137
|
138
|
data TaggedVal val =
|
|
138
|
|
- TaggedVal
|
|
139
|
|
- !val
|
|
140
|
|
- {-# UNPACK #-} !Int -- ^ insertion tag
|
|
|
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].
|
|
141
|
144
|
deriving stock (Data, Functor, Foldable, Traversable)
|
|
142
|
145
|
|
|
143
|
146
|
taggedFst :: TaggedVal val -> val
|
|
144
|
147
|
taggedFst (TaggedVal v _) = v
|
|
|
148
|
+taggedFst TaggedHole = panic "taggedFst: TaggedHole"
|
|
145
|
149
|
|
|
146
|
150
|
taggedSnd :: TaggedVal val -> Int
|
|
147
|
151
|
taggedSnd (TaggedVal _ i) = i
|
|
|
152
|
+taggedSnd TaggedHole = panic "taggedSnd: TaggedHole"
|
|
148
|
153
|
|
|
149
|
154
|
instance Eq val => Eq (TaggedVal val) where
|
|
150
|
155
|
(TaggedVal v1 _) == (TaggedVal v2 _) = v1 == v2
|
|
|
156
|
+ _ == _ = panic "TaggedVal (==): TaggedHole"
|
|
151
|
157
|
|
|
152
|
158
|
-- | Type of unique deterministic finite maps
|
|
153
|
159
|
--
|
| ... |
... |
@@ -207,7 +213,7 @@ addToUDFM_Directly :: UniqDFM key elt -> Unique -> elt -> UniqDFM key elt |
|
207
|
213
|
addToUDFM_Directly (UDFM m i) u v
|
|
208
|
214
|
= UDFM (MS.insertWith tf (getKey u) (TaggedVal v i) m) (i + 1)
|
|
209
|
215
|
where
|
|
210
|
|
- tf (TaggedVal new_v _) (TaggedVal _ old_i) = TaggedVal new_v old_i
|
|
|
216
|
+ tf new old = TaggedVal (taggedFst new) (taggedSnd old)
|
|
211
|
217
|
-- Keep the old tag, but insert the new value
|
|
212
|
218
|
-- This means that udfmToList typically returns elements
|
|
213
|
219
|
-- in the order of insertion, rather than the reverse
|
| ... |
... |
@@ -224,8 +230,8 @@ addToUDFM_C_Directly |
|
224
|
230
|
addToUDFM_C_Directly f (UDFM m i) u v
|
|
225
|
231
|
= UDFM (MS.insertWith tf (getKey u) (TaggedVal v i) m) (i + 1)
|
|
226
|
232
|
where
|
|
227
|
|
- tf (TaggedVal new_v _) (TaggedVal old_v old_i)
|
|
228
|
|
- = TaggedVal (f old_v new_v) old_i
|
|
|
233
|
+ tf new old
|
|
|
234
|
+ = TaggedVal (f (taggedFst old) (taggedFst new)) (taggedSnd old)
|
|
229
|
235
|
-- Flip the arguments, because M.insertWith uses (new->old->result)
|
|
230
|
236
|
-- but f needs (old->new->result)
|
|
231
|
237
|
-- Like addToUDFM_Directly, keep the old tag
|
| ... |
... |
@@ -378,7 +384,7 @@ foldWithKeyUDFM k z m = foldr (uncurry k) z (udfmToList m) |
|
378
|
384
|
nonDetStrictFoldUDFM :: (elt -> a -> a) -> a -> UniqDFM key elt -> a
|
|
379
|
385
|
nonDetStrictFoldUDFM k z (UDFM m _i) = foldl' k' z m
|
|
380
|
386
|
where
|
|
381
|
|
- k' acc (TaggedVal v _) = k v acc
|
|
|
387
|
+ k' acc tv = k (taggedFst tv) acc
|
|
382
|
388
|
|
|
383
|
389
|
{- Note [Cost of deterministic iteration]
|
|
384
|
390
|
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
| ... |
... |
@@ -440,22 +446,13 @@ folding the sorted elements with no intermediate list. foldUDFM is INLINE and |
|
440
|
446
|
dispatches through the out-of-line fold_elts_nonempty, where the fusion happens
|
|
441
|
447
|
once -- so the fold is list-free without inlining the sort into every consumer.
|
|
442
|
448
|
|
|
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 on tag < 0 and
|
|
445
|
|
-never reads the sentinel's value field. But that field is strict, so the
|
|
446
|
|
-sentinel still needs a value in WHNF: a panic thunk is out (it would be forced
|
|
447
|
|
--- and crash -- the moment the readout inspects a hole's tag). We use
|
|
448
|
|
-@unsafeCoerce ()@: () is a static, already-evaluated nullary constructor, so
|
|
449
|
|
-the sentinel is one shared top-level value -- no per-call allocation, and it
|
|
450
|
|
-retains nothing.
|
|
451
|
|
-
|
|
452
|
|
-The unsafeCoerce is safe here because r is always a lifted, boxed (pointer)
|
|
453
|
|
-type -- it comes from the map's elements -- so a pointer to () has the right
|
|
454
|
|
-representation. The GC only ever traces that pointer (() is a valid closure);
|
|
455
|
|
-the value is never evaluated or used as an r, since the readout takes values
|
|
456
|
|
-only from filled slots. Borrowing a real element instead would also type-check
|
|
457
|
|
-but costs a per-call thunk that retains the whole source map until the holes
|
|
458
|
|
-are read.
|
|
|
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.
|
|
459
|
456
|
|
|
460
|
457
|
This sorting method loses when ub is much larger than n = M.size m: ub never
|
|
461
|
458
|
shrinks (overwrites keep bumping it, delete/filter shrink n but not ub). We
|
| ... |
... |
@@ -487,10 +484,10 @@ placementSort :: forall e r. Int |
|
487
|
484
|
-- consuming fold. See Note [Sorting a UDFM].
|
|
488
|
485
|
placementSort ub mk m = build gen
|
|
489
|
486
|
where
|
|
490
|
|
- -- The tag -1 marks unfilled slots; the value field is never read, but it
|
|
491
|
|
- -- is strict, so it needs a WHNF value of type r. See Note [Sorting a UDFM].
|
|
|
487
|
+ -- Unfilled slots hold the TaggedHole sentinel; the readout skips it.
|
|
|
488
|
+ -- See Note [Sorting a UDFM].
|
|
492
|
489
|
hole :: TaggedVal r
|
|
493
|
|
- hole = TaggedVal (unsafeCoerce ()) (-1)
|
|
|
490
|
+ hole = TaggedHole
|
|
494
|
491
|
|
|
495
|
492
|
fill :: SmallMutableArray s (TaggedVal r) -> State# s -> (# State# s, () #)
|
|
496
|
493
|
fill marr s = case M.traverseWithKey_ write m of ST st -> st s
|
| ... |
... |
@@ -511,17 +508,17 @@ placementSort ub mk m = build gen |
|
511
|
508
|
readout :: SmallArray (TaggedVal r) -> Int -> b
|
|
512
|
509
|
readout arr j
|
|
513
|
510
|
| j >= ub = nil
|
|
514
|
|
- | t < 0 = readout arr (j + 1)
|
|
515
|
|
- | otherwise = cons v (readout arr (j + 1))
|
|
516
|
|
- where TaggedVal v t = indexSmallArray arr j
|
|
|
511
|
+ | otherwise = case indexSmallArray arr j of
|
|
|
512
|
+ TaggedHole -> readout arr (j + 1)
|
|
|
513
|
+ TaggedVal v _ -> cons v (readout arr (j + 1))
|
|
517
|
514
|
|
|
518
|
515
|
filterUDFM :: (elt -> Bool) -> UniqDFM key elt -> UniqDFM key elt
|
|
519
|
|
-filterUDFM p (UDFM m i) = UDFM (M.filter (\(TaggedVal v _) -> p v) m) i
|
|
|
516
|
+filterUDFM p (UDFM m i) = UDFM (M.filter (p . taggedFst) m) i
|
|
520
|
517
|
|
|
521
|
518
|
filterUDFM_Directly :: (Unique -> elt -> Bool) -> UniqDFM key elt -> UniqDFM key elt
|
|
522
|
519
|
filterUDFM_Directly p (UDFM m i) = UDFM (M.filterWithKey p' m) i
|
|
523
|
520
|
where
|
|
524
|
|
- p' k (TaggedVal v _) = p (mkUniqueGrimily k) v
|
|
|
521
|
+ p' k tv = p (mkUniqueGrimily k) (taggedFst tv)
|
|
525
|
522
|
|
|
526
|
523
|
udfmRestrictKeys :: UniqDFM key elt -> UniqDFM key elt2 -> UniqDFM key elt
|
|
527
|
524
|
udfmRestrictKeys (UDFM a i) (UDFM b _) = UDFM (M.restrictKeys a (M.keysSet b)) i
|
| ... |
... |
@@ -645,7 +642,7 @@ alterUDFM f (UDFM m i) k = |
|
645
|
642
|
UDFM (M.alter alterf (getKey $ getUnique k) m) (i + 1)
|
|
646
|
643
|
where
|
|
647
|
644
|
alterf Nothing = inject $ f Nothing
|
|
648
|
|
- alterf (Just (TaggedVal v _)) = inject $ f (Just v)
|
|
|
645
|
+ alterf (Just tv) = inject $ f (Just (taggedFst tv))
|
|
649
|
646
|
inject Nothing = Nothing
|
|
650
|
647
|
inject (Just v) = Just $ TaggedVal v i
|
|
651
|
648
|
|
| ... |
... |
@@ -664,7 +661,7 @@ upsertUDFM f (UDFM m i) k = |
|
664
|
661
|
UDFM (MS.upsert upsertf (getKey $ getUnique k) m) (i + 1)
|
|
665
|
662
|
where
|
|
666
|
663
|
upsertf Nothing = TaggedVal (f Nothing) i
|
|
667
|
|
- upsertf (Just (TaggedVal v _)) = TaggedVal (f (Just v)) i
|
|
|
664
|
+ upsertf (Just tv) = TaggedVal (f (Just (taggedFst tv))) i
|
|
668
|
665
|
|
|
669
|
666
|
-- | The expression (@'alterUDFM_L' f map k@) alters value @x@ at @k@, or absence
|
|
670
|
667
|
-- thereof and returns the new element at @k@ if there is any.
|
| ... |
... |
@@ -687,7 +684,7 @@ alterUDFM_L f (UDFM m i) k = |
|
687
|
684
|
where
|
|
688
|
685
|
alterf :: Maybe (TaggedVal elt) -> (Maybe (TaggedVal elt))
|
|
689
|
686
|
alterf Nothing = inject $ f Nothing
|
|
690
|
|
- alterf (Just (TaggedVal v _)) = inject $ f (Just v)
|
|
|
687
|
+ alterf (Just tv) = inject $ f (Just (taggedFst tv))
|
|
691
|
688
|
inject Nothing = Nothing
|
|
692
|
689
|
inject (Just v) = Just $ TaggedVal v i
|
|
693
|
690
|
|