Skip to content

Commit fa9c93e

Browse files
author
Peiming Liu
committed
[mlir][sparse] Remove view-based sparse tensor collapse_shape implementation.
We will migrate to a cleaner and more complete implementation. Reviewed By: aartbik Differential Revision: https://reviews.llvm.org/D158658
1 parent d6639f8 commit fa9c93e

File tree

3 files changed

+73
-215
lines changed

3 files changed

+73
-215
lines changed

mlir/lib/Dialect/SparseTensor/Transforms/LoopEmitter.cpp

Lines changed: 69 additions & 173 deletions
Original file line numberDiff line numberDiff line change
@@ -263,23 +263,15 @@ Value LoopEmitter::genSegmentHigh(OpBuilder &builder, Location loc,
263263
}
264264

265265
Value LoopEmitter::genSparseCrd(OpBuilder &builder, Location loc, TensorId tid,
266-
Level dstLvl) {
266+
Level lvl) {
267267
Value crd = C_IDX(0);
268-
const auto reassoc = getCollapseReassociation(tid, dstLvl);
269-
const unsigned reassocSize = reassoc.size();
270-
for (unsigned i = 0; i < reassocSize; i++) {
271-
const Level srcLvl = reassoc[i];
272-
// A load on the coordinates array yields the coordinate.
273-
const Value mem = coordinatesBuffers[tid][srcLvl];
274-
/// FIXME: See the [CLARIFY_POSITS_LVL] note in the header.
275-
const Value pos = posits[tid][dstLvl];
276-
const Value off = genIndexLoad(builder, loc, mem, pos);
277-
// Linearized the coordinates within the same collapse reassociation.
278-
crd = ADDI(crd, off);
279-
if (i != reassocSize - 1) {
280-
crd = MULI(crd, this->lvlSizes[tid][reassoc[i + 1]]);
281-
}
282-
}
268+
// A load on the coordinates array yields the coordinate.
269+
const Value mem = coordinatesBuffers[tid][lvl];
270+
/// FIXME: See the [CLARIFY_POSITS_LVL] note in the header.
271+
const Value pos = posits[tid][lvl];
272+
const Value off = genIndexLoad(builder, loc, mem, pos);
273+
// Linearized the coordinates within the same collapse reassociation.
274+
crd = ADDI(crd, off);
283275
return crd;
284276
}
285277

@@ -312,7 +304,6 @@ void LoopEmitter::initialize(ValueRange ts, StringAttr loopTag, bool hasOutput,
312304
this->positionsBuffers.assign(numTensors, std::vector<Value>());
313305
this->coordinatesBuffers.assign(numTensors, std::vector<Value>());
314306
this->valBuffer.assign(numTensors, nullptr);
315-
this->collapseReassoc.assign(numTensors, nullptr);
316307
this->isSparseSlices.assign(numTensors, false);
317308
this->sliceOffsets.assign(numTensors, std::vector<Value>());
318309
this->sliceStrides.assign(numTensors, std::vector<Value>());
@@ -348,16 +339,6 @@ void LoopEmitter::initialize(ValueRange ts, StringAttr loopTag, bool hasOutput,
348339
continue;
349340

350341
auto rtp = getRankedTensorType(t);
351-
if (auto reshape = t.getDefiningOp<tensor::CollapseShapeOp>();
352-
isUniqueCOOType(rtp) && reshape) {
353-
// TODO: Supports more kinds of sparse tensors.
354-
// FIXME: We should instead lower reshape operations on sparse tensors
355-
// to view change.
356-
collapseReassoc[tid] = reshape.getReassociation();
357-
rtp = reshape.getSrcType();
358-
// Overwrites the tensor to the source tensor of reshape operations.
359-
tensors[tid] = reshape.getSrc();
360-
}
361342
const SparseTensorType stt(rtp);
362343
lvlRank = stt.getLvlRank();
363344

@@ -394,16 +375,11 @@ void LoopEmitter::initialize(ValueRange ts, StringAttr loopTag, bool hasOutput,
394375
/*offset=*/Value(), /*isNonEmpty*/ Value(),
395376
std::nullopt, 0);
396377
if (dimGetter && !isSynTensor(tid)) {
397-
auto reassoc = collapseReassoc[tid];
398-
Level dstRank = reassoc ? reassoc.size() : lvlRank;
399-
for (Level l = 0; l < dstRank; l++) {
378+
for (Level l = 0; l < lvlRank; l++) {
400379
dependentLvlMap[tid][l] = dimGetter(tid, l);
401380
unsigned depends = dependentLvlMap[tid][l].size();
402381
if (depends == 0)
403382
continue;
404-
// TODO: View-base collapse and dependent index reduction are not
405-
// compatible right now.
406-
assert(!reassoc);
407383
// We need `depends - 1` slices to fully the affine expression.
408384
sliceSizes[tid][l].assign(depends - 1, nullptr);
409385
slicePosBuffer[tid][l].assign(depends - 1, nullptr);
@@ -645,23 +621,18 @@ Value LoopEmitter::genAffine(OpBuilder &builder, Location loc, AffineExpr a) {
645621
}
646622

647623
std::pair<Operation *, Value> LoopEmitter::emitForLoopOverTensorAtLvl(
648-
OpBuilder &builder, Location loc, TensorId tid, Level dstLvl, Value lo,
624+
OpBuilder &builder, Location loc, TensorId tid, Level lvl, Value lo,
649625
Value hi, MutableArrayRef<Value> reduc, bool isParallel) {
650-
bool isSparseCond = isCompressedDLT(lvlTypes[tid][dstLvl]) ||
651-
isCompressedWithHiDLT(lvlTypes[tid][dstLvl]) ||
652-
isSingletonDLT(lvlTypes[tid][dstLvl]);
653-
654-
const auto reassoc = getCollapseReassociation(tid, dstLvl);
626+
bool isSparseCond = isCompressedDLT(lvlTypes[tid][lvl]) ||
627+
isCompressedWithHiDLT(lvlTypes[tid][lvl]) ||
628+
isSingletonDLT(lvlTypes[tid][lvl]);
655629
// TODO: support dynamic slices.
656630
// Uses the first dimension here to build the loop bound (which is also the
657631
// biggest range).
658-
const Level srcLvl = reassoc.front();
659632
Value step = C_IDX(1);
660-
661633
Operation *loop = nullptr;
662634
Value iv;
663635
if (isParallel) {
664-
assert(collapseReassoc[tid] == nullptr);
665636
scf::ParallelOp parOp =
666637
builder.create<scf::ParallelOp>(loc, lo, hi, step, reduc);
667638
builder.setInsertionPointToStart(parOp.getBody());
@@ -693,12 +664,10 @@ std::pair<Operation *, Value> LoopEmitter::emitForLoopOverTensorAtLvl(
693664

694665
Value crd;
695666
if (isSparseCond) {
696-
assert(reassoc.size() == 1 || isUniqueCOOType(tensors[tid].getType()));
697667
// For COO, the position is the same across consecutive levels.
698668
/// FIXME: See the [CLARIFY_POSITS_LVL] note in the header.
699-
llvm::for_each(reassoc,
700-
[this, tid, iv](Level srcLvl) { posits[tid][srcLvl] = iv; });
701-
crd = genSparseCrd(builder, loc, tid, dstLvl);
669+
posits[tid][lvl] = iv;
670+
crd = genSparseCrd(builder, loc, tid, lvl);
702671
} else {
703672
// Dense tensor, the coordinate is the inducation variable.
704673
crd = iv;
@@ -711,7 +680,7 @@ std::pair<Operation *, Value> LoopEmitter::emitForLoopOverTensorAtLvl(
711680
for (Value red : reduc)
712681
types.push_back(red.getType());
713682

714-
auto [trans, pred] = genSliceLegitPredicate(builder, loc, crd, tid, srcLvl);
683+
auto [trans, pred] = genSliceLegitPredicate(builder, loc, crd, tid, lvl);
715684
bool hasReduc = !types.empty();
716685
scf::IfOp ifOp = builder.create<scf::IfOp>(loc, types, pred,
717686
/*else*/ hasReduc);
@@ -733,7 +702,7 @@ std::pair<Operation *, Value> LoopEmitter::emitForLoopOverTensorAtLvl(
733702
}
734703

735704
assert(crd);
736-
coords[tid][dstLvl] = crd;
705+
coords[tid][lvl] = crd;
737706
return {loop, crd};
738707
}
739708

@@ -743,11 +712,9 @@ Value LoopEmitter::genWhileLoopConditions(OpBuilder &builder, Location loc,
743712

744713
switch (cond.second) {
745714
case LoopCondKind::SparseCond: {
746-
const auto reassoc = getCollapseReassociation(tid, lvl);
747-
assert(reassoc.size() == ivs.size());
748-
assert(reassoc.size() == 1 || isUniqueCOOType(tensors[tid].getType()));
715+
assert(ivs.size() == 1);
749716
// We used the first level bound as the bound the collapsed set of levels.
750-
return CMPI(ult, ivs.back(), highs[tid][reassoc.front()]);
717+
return CMPI(ult, ivs.back(), highs[tid][lvl]);
751718
}
752719
case LoopCondKind::SparseSliceCond: {
753720
assert(ivs.size() == 1);
@@ -787,17 +754,9 @@ std::optional<Value> LoopEmitter::genWhileLoopBody(OpBuilder &builder,
787754

788755
switch (cond.second) {
789756
case LoopCondKind::SparseCond: {
790-
const auto reassoc = getCollapseReassociation(tid, lvl);
791-
assert(reassoc.size() == 1 || isUniqueCOOType(tensors[tid].getType()));
792-
// Links the SSA chain for segHi.
793-
for (unsigned i = 0, e = reassoc.size() - 1; i < e; i++)
794-
if (!isUniqueDLT(lvlTypes[tid][reassoc[i]]))
795-
segHi[tid][reassoc[i]] = ivs[i];
796-
797757
// Updates position. For collapsed COO, the position is the same across
798758
// consecutive levels.
799-
for (auto srcLvl : reassoc)
800-
posits[tid][srcLvl] = ivs.back();
759+
posits[tid][lvl] = ivs.back();
801760

802761
// Update coordinates.
803762
coords[tid][lvl] = genSparseCrd(builder, loc, tid, lvl);
@@ -883,11 +842,9 @@ std::pair<Operation *, Value> LoopEmitter::emitWhileLoopOverTensorsAtLvls(
883842
(void)lvlTp;
884843

885844
unsigned prevSz = ivs.size();
886-
const auto reassoc = getCollapseReassociation(tid, lvl);
887845
if (isAffineIdxCond(cKind)) {
888846
// TODO: Support view-based reshape on sparse levels with affine index
889847
// expressions.
890-
assert(reassoc.size() == 1);
891848
if (isAffineIdxUnRedCond(cKind)) {
892849
SliceInfo &sliceInfo = sliceStack[tid].back();
893850
// The order matters!
@@ -901,12 +858,7 @@ std::pair<Operation *, Value> LoopEmitter::emitWhileLoopOverTensorsAtLvls(
901858
levelReducedDep[tid][lvl]++;
902859
} else {
903860
assert(dependentLvlMap[tid][lvl].empty());
904-
for (unsigned i = 0, e = reassoc.size() - 1; i < e; i++) {
905-
// This is the segment high for each non-unique levels.
906-
if (!isUniqueDLT(lvlTypes[tid][reassoc[i]]))
907-
ivs.push_back(C_IDX(0));
908-
}
909-
const Value pos = posits[tid][reassoc.front()];
861+
const Value pos = posits[tid][lvl];
910862
ivs.push_back(pos);
911863
}
912864
opSegSize.push_back(ivs.size() - prevSz);
@@ -985,49 +937,11 @@ std::pair<Operation *, Value> LoopEmitter::emitWhileLoopOverTensorsAtLvls(
985937
builder.setInsertionPointToStart(&ifOp.getThenRegion().front());
986938
}
987939

988-
for (auto [tid, dstLvl] : unpackTensorLevelFromCondRange(spConds)) {
989-
const auto reassoc = getCollapseReassociation(tid, dstLvl);
990-
assert(reassoc.size() == 1 || isUniqueCOOType(tensors[tid].getType()));
991-
// TODO: Refactors this into smaller functions.
992-
// NOTE: For all the collapsed level (except for the last one, that is why
993-
// the loop ends with `reassoc.size() - 1`), as each iteration is advanced
994-
// by the segment size of the last level, which does not always invalidate
995-
// the segment size for the previous levels, thus we need to propagate the
996-
// segment sizes across loop iterations and only forward if needed.
997-
//
998-
// E.g., for a COO tensor with the following coordinates array.
999-
// (0, 0, 1),
1000-
// (0, 0, 2),
1001-
// (1, 1, 1),
1002-
// segHi[lvl=0] = segHi[lvl=1] = 2
1003-
// segHi[lvl=2] = 1,
1004-
// the first iteration does not invalidate segHi[0] and segHi[1]
1005-
for (unsigned i = 0, e = reassoc.size() - 1; i < e; i++) {
1006-
const Level srcLvl = reassoc[i];
1007-
if (!isUniqueDLT(lvlTypes[tid][srcLvl])) {
1008-
const Value pos = posits[tid][srcLvl];
1009-
const auto oldSegHi = segHi[tid][srcLvl];
1010-
assert(oldSegHi);
1011-
Value newSegHi = builder.create<arith::CmpIOp>(
1012-
loc, arith::CmpIPredicate::uge, pos, oldSegHi);
1013-
auto ifNewSegHi = builder.create<scf::IfOp>(loc, builder.getIndexType(),
1014-
newSegHi, true);
1015-
{
1016-
OpBuilder::InsertionGuard guard(builder);
1017-
builder.setInsertionPointToStart(ifNewSegHi.thenBlock());
1018-
YIELD(genSegmentHigh(builder, loc, tid, srcLvl, pos,
1019-
highs[tid][srcLvl]));
1020-
// Else, resues the same segment high.
1021-
builder.setInsertionPointToStart(ifNewSegHi.elseBlock());
1022-
YIELD(oldSegHi);
1023-
}
1024-
highs[tid][srcLvl + 1] = segHi[tid][srcLvl] = ifNewSegHi.getResult(0);
1025-
}
1026-
};
1027-
const auto srcLvl = reassoc.back();
1028-
if (!isUniqueDLT(lvlTypes[tid][srcLvl])) {
1029-
segHi[tid][srcLvl] = genSegmentHigh(
1030-
builder, loc, tid, srcLvl, posits[tid][srcLvl], highs[tid][srcLvl]);
940+
for (auto [tid, lvl] : unpackTensorLevelFromCondRange(spConds)) {
941+
// Generates segment high for non-unique level.
942+
if (!isUniqueDLT(lvlTypes[tid][lvl])) {
943+
segHi[tid][lvl] = genSegmentHigh(builder, loc, tid, lvl, posits[tid][lvl],
944+
highs[tid][lvl]);
1031945
}
1032946
}
1033947

@@ -1074,9 +988,8 @@ bool LoopEmitter::shouldIteratedByForLoop(ArrayRef<TensorLvlCond> sparseConds,
1074988
// non-unique levels when deduplication is required.
1075989
if (sparseConds.size() == 1) {
1076990
auto [tid, lvl] = unpackTensorLevel(sparseConds.back().first);
1077-
auto reassoc = getCollapseReassociation(tid, lvl);
1078991
return !isAffineIdxCond(sparseConds.back().second) &&
1079-
!(genDedup && !isUniqueDLT(lvlTypes[tid][reassoc.back()]));
992+
!(genDedup && !isUniqueDLT(lvlTypes[tid][lvl]));
1080993
}
1081994

1082995
return true;
@@ -1245,50 +1158,45 @@ void LoopEmitter::genDenseAffineAddress(OpBuilder &builder, Location loc,
12451158
}
12461159

12471160
void LoopEmitter::prepareLoopOverTensorAtLvl(OpBuilder &builder, Location loc,
1248-
TensorId tid, Level dstLvl) {
1249-
assert(isValidLevel(tid, dstLvl));
1250-
const auto lvlTp = lvlTypes[tid][dstLvl];
1161+
TensorId tid, Level lvl) {
1162+
assert(isValidLevel(tid, lvl));
1163+
const auto lvlTp = lvlTypes[tid][lvl];
12511164

12521165
if (isDenseDLT(lvlTp))
12531166
return;
12541167

12551168
const Value c0 = C_IDX(0);
12561169
const Value c1 = C_IDX(1);
1257-
for (const Level srcLvl : getCollapseReassociation(tid, dstLvl)) {
1258-
// Either the first level, or the previous level has been set.
1259-
/// FIXME: See the [CLARIFY_POSITS_LVL] note in the header.
1260-
assert(srcLvl == 0 || posits[tid][srcLvl - 1]);
1261-
if (isDenseDLT(lvlTp))
1262-
continue;
1263-
if (isCompressedDLT(lvlTp) || isCompressedWithHiDLT(lvlTp)) {
1264-
const Value mem = positionsBuffers[tid][srcLvl];
1265-
1266-
Value pLo = srcLvl == 0 ? c0 : posits[tid][srcLvl - 1];
1267-
if (isCompressedWithHiDLT(lvlTp))
1268-
pLo = builder.create<arith::MulIOp>(loc, pLo, C_IDX(2));
1269-
posits[tid][srcLvl] = genIndexLoad(builder, loc, mem, pLo);
1270-
1271-
const Value pHi = ADDI(pLo, c1);
1272-
highs[tid][srcLvl] = genIndexLoad(builder, loc, mem, pHi);
1273-
return;
1274-
}
1275-
if (isSingletonDLT(lvlTp)) {
1276-
const Value pLo = srcLvl == 0 ? c0 : posits[tid][srcLvl - 1];
1277-
posits[tid][srcLvl] = pLo;
1278-
1279-
// If we are coiterating non-unique levels, then use pHi=segHi;
1280-
// otherwise use pHi=pLo+1.
1281-
// NOTE: Just because the level is non-unique, that does not
1282-
// guarantee that segHi is defined: because we only generate segHi
1283-
// whenever coiterating, in order to improve code quality for the
1284-
// non-coiterating cases.
1285-
const auto parentSegHi = segHi[tid][srcLvl - 1];
1286-
highs[tid][srcLvl] =
1287-
(!isUniqueDLT(lvlTypes[tid][srcLvl - 1]) && parentSegHi)
1288-
? parentSegHi
1289-
: ADDI(pLo, c1);
1290-
return;
1291-
}
1170+
// Either the first level, or the previous level has been set.
1171+
/// FIXME: See the [CLARIFY_POSITS_LVL] note in the header.
1172+
assert(lvl == 0 || posits[tid][lvl - 1]);
1173+
if (isCompressedDLT(lvlTp) || isCompressedWithHiDLT(lvlTp)) {
1174+
const Value mem = positionsBuffers[tid][lvl];
1175+
1176+
Value pLo = lvl == 0 ? c0 : posits[tid][lvl - 1];
1177+
if (isCompressedWithHiDLT(lvlTp))
1178+
pLo = builder.create<arith::MulIOp>(loc, pLo, C_IDX(2));
1179+
posits[tid][lvl] = genIndexLoad(builder, loc, mem, pLo);
1180+
1181+
const Value pHi = ADDI(pLo, c1);
1182+
highs[tid][lvl] = genIndexLoad(builder, loc, mem, pHi);
1183+
return;
1184+
}
1185+
if (isSingletonDLT(lvlTp)) {
1186+
const Value pLo = lvl == 0 ? c0 : posits[tid][lvl - 1];
1187+
posits[tid][lvl] = pLo;
1188+
1189+
// If we are coiterating non-unique levels, then use pHi=segHi;
1190+
// otherwise use pHi=pLo+1.
1191+
// NOTE: Just because the level is non-unique, that does not
1192+
// guarantee that segHi is defined: because we only generate segHi
1193+
// whenever coiterating, in order to improve code quality for the
1194+
// non-coiterating cases.
1195+
const auto parentSegHi = segHi[tid][lvl - 1];
1196+
highs[tid][lvl] = (!isUniqueDLT(lvlTypes[tid][lvl - 1]) && parentSegHi)
1197+
? parentSegHi
1198+
: ADDI(pLo, c1);
1199+
return;
12921200
}
12931201

12941202
llvm_unreachable("Unrecognized level-type!");
@@ -1542,28 +1450,18 @@ void LoopEmitter::exitWhileLoop(OpBuilder &builder, Location loc,
15421450
posits[tid][lvl] = whileOp->getResult(o++);
15431451
};
15441452

1545-
for (auto [tid, dstLvl] : unpackTensorLevelRange(loopInfo.trivialTidLvls)) {
1546-
const auto lvlTp = lvlTypes[tid][dstLvl];
1453+
for (auto [tid, lvl] : unpackTensorLevelRange(loopInfo.trivialTidLvls)) {
1454+
const auto lvlTp = lvlTypes[tid][lvl];
15471455
if (isCompressedDLT(lvlTp) || isSingletonDLT(lvlTp) ||
15481456
isCompressedWithHiDLT(lvlTp)) {
1549-
const auto reassoc = getCollapseReassociation(tid, dstLvl);
1550-
assert(reassoc.size() == 1 || isUniqueCOOType(tensors[tid].getType()));
1551-
for (unsigned i = 0, e = reassoc.size() - 1; i < e; i++) {
1552-
const Level srcLvl = reassoc[i];
1553-
if (!isUniqueDLT(lvlTypes[tid][srcLvl])) {
1554-
operands.push_back(segHi[tid][srcLvl]);
1555-
o++;
1556-
}
1557-
}
1558-
const Value crd = coords[tid][dstLvl];
1559-
const Value pos = posits[tid][dstLvl];
1457+
const Value crd = coords[tid][lvl];
1458+
const Value pos = posits[tid][lvl];
15601459
Value cmp = CMPI(eq, crd, iv);
15611460
// If the loop contains a coiteration with non-unique level, we fast
15621461
// forward all the duplicated coords by setting the position to the
15631462
// segment high.
1564-
Value add = !isUniqueDLT(lvlTypes[tid][reassoc.back()])
1565-
? segHi[tid][reassoc.back()]
1566-
: ADDI(pos, one);
1463+
Value add =
1464+
!isUniqueDLT(lvlTypes[tid][lvl]) ? segHi[tid][lvl] : ADDI(pos, one);
15671465

15681466
operands.push_back(SELECT(cmp, add, pos));
15691467
// Following loops continue iteration from the break point of the
@@ -1573,14 +1471,12 @@ void LoopEmitter::exitWhileLoop(OpBuilder &builder, Location loc,
15731471
// warnings about "captured structured bindings are a C++20 extension".
15741472
// FIXME(wrengr): define a helper function to capture this idiom!
15751473
const TensorId newTid = tid;
1576-
llvm::for_each(reassoc, [this, newTid, newPos](Level srcLvl) {
1577-
posits[newTid][srcLvl] = newPos;
1578-
});
1474+
posits[newTid][lvl] = newPos;
15791475

15801476
// The coordinate is invalid now.
1581-
coords[tid][dstLvl] = nullptr;
1477+
coords[tid][lvl] = nullptr;
15821478
// The segment high is invalid now.
1583-
segHi[tid][dstLvl] = nullptr;
1479+
segHi[tid][lvl] = nullptr;
15841480
// highs remains unchanged.
15851481
}
15861482
}

0 commit comments

Comments
 (0)