@@ -115,11 +115,19 @@ class SparseTensorStorageBase {
115
115
return isCompressedDLT (getLvlType (l));
116
116
}
117
117
118
+ // / Safely checks if the level uses loose compressed storage.
119
+ bool isLooseCompressedLvl (uint64_t l) const {
120
+ return isLooseCompressedDLT (getLvlType (l));
121
+ }
122
+
118
123
// / Safely checks if the level uses singleton storage.
119
124
bool isSingletonLvl (uint64_t l) const {
120
125
return isSingletonDLT (getLvlType (l));
121
126
}
122
127
128
+ // / Safely checks if the level uses 2 out of 4 storage.
129
+ bool is2OutOf4Lvl (uint64_t l) const { return is2OutOf4DLT (getLvlType (l)); }
130
+
123
131
// / Safely checks if the level is ordered.
124
132
bool isOrderedLvl (uint64_t l) const { return isOrderedDLT (getLvlType (l)); }
125
133
@@ -138,9 +146,6 @@ class SparseTensorStorageBase {
138
146
MLIR_SPARSETENSOR_FOREVERY_FIXED_O (DECL_GETCOORDINATES)
139
147
#undef DECL_GETCOORDINATES
140
148
141
- // / Gets the coordinate-value stored at the given level and position.
142
- virtual uint64_t getCrd (uint64_t lvl, uint64_t pos) const = 0;
143
-
144
149
// / Gets primary storage.
145
150
#define DECL_GETVALUES (VNAME, V ) virtual void getValues (std::vector<V> **);
146
151
MLIR_SPARSETENSOR_FOREVERY_V (DECL_GETVALUES)
@@ -280,13 +285,6 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
280
285
*out = &values;
281
286
}
282
287
283
- // / Returns coordinate at given position.
284
- uint64_t getCrd (uint64_t lvl, uint64_t pos) const final {
285
- assert (isCompressedDLT (getLvlType (lvl)) || isSingletonDLT (getLvlType (lvl)));
286
- assert (pos < coordinates[lvl].size ());
287
- return coordinates[lvl][pos]; // Converts the stored `C` into `uint64_t`.
288
- }
289
-
290
288
// / Partially specialize forwarding insertions based on template types.
291
289
void forwardingInsert (const uint64_t *dimCoords, V val) final {
292
290
assert (dimCoords && coo);
@@ -302,7 +300,7 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
302
300
if (allDense) {
303
301
uint64_t lvlRank = getLvlRank ();
304
302
uint64_t valIdx = 0 ;
305
- // Linearize the address
303
+ // Linearize the address.
306
304
for (uint64_t lvl = 0 ; lvl < lvlRank; lvl++)
307
305
valIdx = valIdx * getLvlSize (lvl) + lvlCoords[lvl];
308
306
values[valIdx] = val;
@@ -441,16 +439,6 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
441
439
}
442
440
443
441
private:
444
- // / Appends an arbitrary new position to `positions[lvl]`. This method
445
- // / checks that `pos` is representable in the `P` type; however, it
446
- // / does not check that `pos` is semantically valid (i.e., larger than
447
- // / the previous position and smaller than `coordinates[lvl].capacity()`).
448
- void appendPos (uint64_t lvl, uint64_t pos, uint64_t count = 1 ) {
449
- assert (isCompressedLvl (lvl));
450
- positions[lvl].insert (positions[lvl].end (), count,
451
- detail::checkOverflowCast<P>(pos));
452
- }
453
-
454
442
// / Appends coordinate `crd` to level `lvl`, in the semantically
455
443
// / general sense. For non-dense levels, that means appending to the
456
444
// / `coordinates[lvl]` array, checking that `crd` is representable in
@@ -461,11 +449,11 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
461
449
// / `full` is the number of "entries" already written to `values` for this
462
450
// / segment (aka one after the highest coordinate previously appended).
463
451
void appendCrd (uint64_t lvl, uint64_t full, uint64_t crd) {
464
- const auto dlt = getLvlType (lvl); // Avoid redundant bounds checking.
465
- if (isCompressedDLT (dlt) || isSingletonDLT (dlt)) {
452
+ if (!isDenseLvl (lvl)) {
453
+ assert (isCompressedLvl (lvl) || isLooseCompressedLvl (lvl) ||
454
+ isSingletonLvl (lvl) || is2OutOf4Lvl (lvl));
466
455
coordinates[lvl].push_back (detail::checkOverflowCast<C>(crd));
467
456
} else { // Dense level.
468
- assert (isDenseDLT (dlt));
469
457
assert (crd >= full && " Coordinate was already filled" );
470
458
if (crd == full)
471
459
return ; // Short-circuit, since it'll be a nop.
@@ -482,15 +470,13 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
482
470
// / storage, as opposed to "level-sizes" which are the cardinality
483
471
// / of possible coordinates for that level.
484
472
uint64_t assembledSize (uint64_t parentSz, uint64_t l) const {
485
- const auto dlt = getLvlType (l); // Avoid redundant bounds checking.
486
- if (isCompressedDLT (dlt))
473
+ if (isCompressedLvl (l))
487
474
return positions[l][parentSz];
488
- if (isSingletonDLT (dlt ))
475
+ if (isSingletonLvl (l ))
489
476
return parentSz; // New size is same as the parent.
490
- if (isDenseDLT (dlt))
491
- return parentSz * getLvlSize (l);
492
- MLIR_SPARSETENSOR_FATAL (" unsupported level type: %d\n " ,
493
- static_cast <uint8_t >(dlt));
477
+ // TODO: support levels assignment for loose/2:4?
478
+ assert (isDenseLvl (l));
479
+ return parentSz * getLvlSize (l);
494
480
}
495
481
496
482
// / Initializes sparse tensor storage scheme from a memory-resident sparse
@@ -514,7 +500,7 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
514
500
uint64_t seg = lo + 1 ;
515
501
if (isUniqueLvl (l))
516
502
while (seg < hi && lvlElements[seg].coords [l] == c)
517
- ++seg ;
503
+ seg++ ;
518
504
// Handle segment in interval for sparse or dense level.
519
505
appendCrd (l, full, c);
520
506
full = c + 1 ;
@@ -529,14 +515,22 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
529
515
// / Finalizes the sparse position structure at this level.
530
516
void finalizeSegment (uint64_t l, uint64_t full = 0 , uint64_t count = 1 ) {
531
517
if (count == 0 )
532
- return ; // Short-circuit, since it'll be a nop.
533
- const auto dlt = getLvlType (l); // Avoid redundant bounds checking.
534
- if (isCompressedDLT (dlt)) {
535
- appendPos (l, coordinates[l].size (), count);
536
- } else if (isSingletonDLT (dlt)) {
518
+ return ; // Short-circuit, since it'll be a nop.
519
+ if (isCompressedLvl (l)) {
520
+ uint64_t pos = coordinates[l].size ();
521
+ positions[l].insert (positions[l].end (), count,
522
+ detail::checkOverflowCast<P>(pos));
523
+ } else if (isLooseCompressedLvl (l)) {
524
+ // Finish this level, and push pairs for the empty ones, and one
525
+ // more for next level. Note that this always leaves one extra
526
+ // unused element at the end.
527
+ uint64_t pos = coordinates[l].size ();
528
+ positions[l].insert (positions[l].end (), 2 * count,
529
+ detail::checkOverflowCast<P>(pos));
530
+ } else if (isSingletonLvl (l) || is2OutOf4Lvl (l)) {
537
531
return ; // Nothing to finalize.
538
532
} else { // Dense dimension.
539
- assert (isDenseDLT (dlt ));
533
+ assert (isDenseLvl (l ));
540
534
const uint64_t sz = getLvlSizes ()[l];
541
535
assert (sz >= full && " Segment is overfull" );
542
536
count = detail::checkedMul (count, sz - full);
@@ -589,7 +583,6 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
589
583
(crd < cur && !isOrderedLvl (l))) {
590
584
return l;
591
585
}
592
-
593
586
if (crd < cur) {
594
587
assert (false && " non-lexicographic insertion" );
595
588
return -1u ;
@@ -609,27 +602,37 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
609
602
return ;
610
603
}
611
604
if (isCompressedLvl (l)) {
612
- // Look up the bounds of the `l`-level segment determined by the
613
- // `(l - 1)`-level position `parentPos`.
614
605
const std::vector<P> &positionsL = positions[l];
615
606
assert (parentPos + 1 < positionsL.size ());
616
607
const uint64_t pstart = static_cast <uint64_t >(positionsL[parentPos]);
617
608
const uint64_t pstop = static_cast <uint64_t >(positionsL[parentPos + 1 ]);
618
- // Loop-invariant code for looking up the `l`-level coordinates.
619
609
const std::vector<C> &coordinatesL = coordinates[l];
620
610
assert (pstop <= coordinatesL.size ());
621
- for (uint64_t pos = pstart; pos < pstop; ++pos ) {
611
+ for (uint64_t pos = pstart; pos < pstop; pos++ ) {
622
612
lvlCursor[l] = static_cast <uint64_t >(coordinatesL[pos]);
623
613
toCOO (pos, l + 1 , dimCoords);
624
614
}
625
- } else if (isSingletonLvl (l)) {
626
- lvlCursor[l] = getCrd (l, parentPos);
615
+ } else if (isLooseCompressedLvl (l)) {
616
+ const std::vector<P> &positionsL = positions[l];
617
+ assert (2 * parentPos + 1 < positionsL.size ());
618
+ const uint64_t pstart = static_cast <uint64_t >(positionsL[2 * parentPos]);
619
+ const uint64_t pstop =
620
+ static_cast <uint64_t >(positionsL[2 * parentPos + 1 ]);
621
+ const std::vector<C> &coordinatesL = coordinates[l];
622
+ assert (pstop <= coordinatesL.size ());
623
+ for (uint64_t pos = pstart; pos < pstop; pos++) {
624
+ lvlCursor[l] = static_cast <uint64_t >(coordinatesL[pos]);
625
+ toCOO (pos, l + 1 , dimCoords);
626
+ }
627
+ } else if (isSingletonLvl (l) || is2OutOf4Lvl (l)) {
628
+ assert (parentPos < coordinates[l].size ());
629
+ lvlCursor[l] = static_cast <uint64_t >(coordinates[l][parentPos]);
627
630
toCOO (parentPos, l + 1 , dimCoords);
628
631
} else { // Dense level.
629
632
assert (isDenseLvl (l));
630
633
const uint64_t sz = getLvlSizes ()[l];
631
634
const uint64_t pstart = parentPos * sz;
632
- for (uint64_t c = 0 ; c < sz; ++c ) {
635
+ for (uint64_t c = 0 ; c < sz; c++ ) {
633
636
lvlCursor[l] = c;
634
637
toCOO (pstart + c, l + 1 , dimCoords);
635
638
}
@@ -706,19 +709,30 @@ SparseTensorStorage<P, C, V>::SparseTensorStorage(
706
709
bool allDense = true ;
707
710
uint64_t sz = 1 ;
708
711
for (uint64_t l = 0 ; l < lvlRank; l++) {
709
- const DimLevelType dlt = lvlTypes[l]; // Avoid redundant bounds checking.
710
- if (isCompressedDLT (dlt)) {
712
+ if (isCompressedLvl (l)) {
711
713
positions[l].reserve (sz + 1 );
712
714
positions[l].push_back (0 );
713
715
coordinates[l].reserve (sz);
714
716
sz = 1 ;
715
717
allDense = false ;
716
- } else if (isSingletonDLT (dlt)) {
718
+ } else if (isLooseCompressedLvl (l)) {
719
+ positions[l].reserve (2 * sz + 1 ); // last one unused
720
+ positions[l].push_back (0 );
717
721
coordinates[l].reserve (sz);
718
722
sz = 1 ;
719
723
allDense = false ;
724
+ } else if (isSingletonLvl (l)) {
725
+ coordinates[l].reserve (sz);
726
+ sz = 1 ;
727
+ allDense = false ;
728
+ } else if (is2OutOf4Lvl (l)) {
729
+ assert (allDense && l == lvlRank - 1 && " unexpected 2:4 usage" );
730
+ sz = detail::checkedMul (sz, lvlSizes[l]) / 2 ;
731
+ coordinates[l].reserve (sz);
732
+ values.reserve (sz);
733
+ allDense = false ;
720
734
} else { // Dense level.
721
- assert (isDenseDLT (dlt ));
735
+ assert (isDenseLvl (l ));
722
736
sz = detail::checkedMul (sz, lvlSizes[l]);
723
737
}
724
738
}
@@ -773,6 +787,7 @@ SparseTensorStorage<P, C, V>::SparseTensorStorage(
773
787
positions[l].assign (posPtr, posPtr + parentSz + 1 );
774
788
coordinates[l].assign (crdPtr, crdPtr + positions[l][parentSz]);
775
789
} else {
790
+ // TODO: support levels assignment for loose/2:4?
776
791
assert (isDenseLvl (l));
777
792
}
778
793
parentSz = assembledSize (parentSz, l);
0 commit comments