Skip to content

Commit af8428c

Browse files
authored
[mlir][sparse] unify support of (dis)assemble between direct IR/lib path (#71880)
Note that the (dis)assemble operations still make some simplfying assumptions (e.g. trailing 2-D COO in AoS format) but now at least both the direct IR and support library path behave exactly the same. Generalizing the ops is still TBD.
1 parent 5fdb70b commit af8428c

File tree

9 files changed

+310
-295
lines changed

9 files changed

+310
-295
lines changed

mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -295,9 +295,15 @@ class SparseTensorType {
295295
// `getLvlType` method instead of STEA's.
296296
bool isDenseLvl(Level l) const { return isDenseDLT(getLvlType(l)); }
297297
bool isCompressedLvl(Level l) const { return isCompressedDLT(getLvlType(l)); }
298+
bool isLooseCompressedLvl(Level l) const {
299+
return isLooseCompressedDLT(getLvlType(l));
300+
}
298301
bool isSingletonLvl(Level l) const { return isSingletonDLT(getLvlType(l)); }
302+
bool is2OutOf4Lvl(Level l) const { return is2OutOf4DLT(getLvlType(l)); }
299303
bool isOrderedLvl(Level l) const { return isOrderedDLT(getLvlType(l)); }
300304
bool isUniqueLvl(Level l) const { return isUniqueDLT(getLvlType(l)); }
305+
bool isWithPos(Level l) const { return isDLTWithPos(getLvlType(l)); }
306+
bool isWithCrd(Level l) const { return isDLTWithCrd(getLvlType(l)); }
301307

302308
/// Returns the coordinate-overhead bitwidth, defaulting to zero.
303309
unsigned getCrdWidth() const { return enc ? enc.getCrdWidth() : 0; }

mlir/include/mlir/ExecutionEngine/SparseTensor/Storage.h

Lines changed: 43 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -301,8 +301,8 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
301301
uint64_t lvlRank = getLvlRank();
302302
uint64_t valIdx = 0;
303303
// Linearize the address.
304-
for (uint64_t lvl = 0; lvl < lvlRank; lvl++)
305-
valIdx = valIdx * getLvlSize(lvl) + lvlCoords[lvl];
304+
for (uint64_t l = 0; l < lvlRank; l++)
305+
valIdx = valIdx * getLvlSize(l) + lvlCoords[l];
306306
values[valIdx] = val;
307307
return;
308308
}
@@ -472,9 +472,10 @@ class SparseTensorStorage final : public SparseTensorStorageBase {
472472
uint64_t assembledSize(uint64_t parentSz, uint64_t l) const {
473473
if (isCompressedLvl(l))
474474
return positions[l][parentSz];
475-
if (isSingletonLvl(l))
476-
return parentSz; // New size is same as the parent.
477-
// TODO: support levels assignment for loose/2:4?
475+
if (isLooseCompressedLvl(l))
476+
return positions[l][2 * parentSz - 1];
477+
if (isSingletonLvl(l) || is2OutOf4Lvl(l))
478+
return parentSz; // new size same as the parent
478479
assert(isDenseLvl(l));
479480
return parentSz * getLvlSize(l);
480481
}
@@ -766,40 +767,59 @@ SparseTensorStorage<P, C, V>::SparseTensorStorage(
766767
const uint64_t *dim2lvl, const uint64_t *lvl2dim, const intptr_t *lvlBufs)
767768
: SparseTensorStorage(dimRank, dimSizes, lvlRank, lvlSizes, lvlTypes,
768769
dim2lvl, lvl2dim) {
770+
// Note that none of the buffers can be reused because ownership
771+
// of the memory passed from clients is not necessarily transferred.
772+
// Therefore, all data is copied over into a new SparseTensorStorage.
773+
//
774+
// TODO: this needs to be generalized to all formats AND
775+
// we need a proper audit of e.g. double compressed
776+
// levels where some are not filled
777+
//
769778
uint64_t trailCOOLen = 0, parentSz = 1, bufIdx = 0;
770779
for (uint64_t l = 0; l < lvlRank; l++) {
771-
if (!isUniqueLvl(l) && isCompressedLvl(l)) {
772-
// A `compressed_nu` level marks the start of trailing COO start level.
773-
// Since the coordinate buffer used for trailing COO are passed in as AoS
774-
// scheme, and SparseTensorStorage uses a SoA scheme, we can not simply
775-
// copy the value from the provided buffers.
780+
if (!isUniqueLvl(l) && (isCompressedLvl(l) || isLooseCompressedLvl(l))) {
781+
// A `(loose)compressed_nu` level marks the start of trailing COO
782+
// start level. Since the coordinate buffer used for trailing COO
783+
// is passed in as AoS scheme and SparseTensorStorage uses a SoA
784+
// scheme, we cannot simply copy the value from the provided buffers.
776785
trailCOOLen = lvlRank - l;
777786
break;
778787
}
779-
assert(!isSingletonLvl(l) &&
780-
"Singleton level not following a compressed_nu level");
781-
if (isCompressedLvl(l)) {
788+
if (isCompressedLvl(l) || isLooseCompressedLvl(l)) {
782789
P *posPtr = reinterpret_cast<P *>(lvlBufs[bufIdx++]);
783790
C *crdPtr = reinterpret_cast<C *>(lvlBufs[bufIdx++]);
784-
// Copies the lvlBuf into the vectors. The buffer can not be simply reused
785-
// because the memory passed from users is not necessarily allocated on
786-
// heap.
787-
positions[l].assign(posPtr, posPtr + parentSz + 1);
788-
coordinates[l].assign(crdPtr, crdPtr + positions[l][parentSz]);
791+
if (isLooseCompressedLvl(l)) {
792+
positions[l].assign(posPtr, posPtr + 2 * parentSz);
793+
coordinates[l].assign(crdPtr, crdPtr + positions[l][2 * parentSz - 1]);
794+
} else {
795+
positions[l].assign(posPtr, posPtr + parentSz + 1);
796+
coordinates[l].assign(crdPtr, crdPtr + positions[l][parentSz]);
797+
}
798+
} else if (isSingletonLvl(l)) {
799+
assert(0 && "general singleton not supported yet");
800+
} else if (is2OutOf4Lvl(l)) {
801+
assert(0 && "2Out4 not supported yet");
789802
} else {
790-
// TODO: support levels assignment for loose/2:4?
791803
assert(isDenseLvl(l));
792804
}
793805
parentSz = assembledSize(parentSz, l);
794806
}
795807

808+
// Handle Aos vs. SoA mismatch for COO.
796809
if (trailCOOLen != 0) {
797810
uint64_t cooStartLvl = lvlRank - trailCOOLen;
798-
assert(!isUniqueLvl(cooStartLvl) && isCompressedLvl(cooStartLvl));
811+
assert(!isUniqueLvl(cooStartLvl) &&
812+
(isCompressedLvl(cooStartLvl) || isLooseCompressedLvl(cooStartLvl)));
799813
P *posPtr = reinterpret_cast<P *>(lvlBufs[bufIdx++]);
800814
C *aosCrdPtr = reinterpret_cast<C *>(lvlBufs[bufIdx++]);
801-
positions[cooStartLvl].assign(posPtr, posPtr + parentSz + 1);
802-
P crdLen = positions[cooStartLvl][parentSz];
815+
P crdLen;
816+
if (isLooseCompressedLvl(cooStartLvl)) {
817+
positions[cooStartLvl].assign(posPtr, posPtr + 2 * parentSz);
818+
crdLen = positions[cooStartLvl][2 * parentSz - 1];
819+
} else {
820+
positions[cooStartLvl].assign(posPtr, posPtr + parentSz + 1);
821+
crdLen = positions[cooStartLvl][parentSz];
822+
}
803823
for (uint64_t l = cooStartLvl; l < lvlRank; l++) {
804824
coordinates[l].resize(crdLen);
805825
for (uint64_t n = 0; n < crdLen; n++) {
@@ -809,6 +829,7 @@ SparseTensorStorage<P, C, V>::SparseTensorStorage(
809829
parentSz = assembledSize(parentSz, cooStartLvl);
810830
}
811831

832+
// Copy the values buffer.
812833
V *valPtr = reinterpret_cast<V *>(lvlBufs[bufIdx]);
813834
values.assign(valPtr, valPtr + parentSz);
814835
}

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

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,17 @@ Value sparse_tensor::genCast(OpBuilder &builder, Location loc, Value value,
163163
return mlir::convertScalarToDtype(builder, loc, value, dstTp, isUnsignedCast);
164164
}
165165

166+
Value sparse_tensor::genScalarToTensor(OpBuilder &builder, Location loc,
167+
Value elem, Type dstTp) {
168+
if (auto rtp = dstTp.dyn_cast<RankedTensorType>()) {
169+
// Scalars can only be converted to 0-ranked tensors.
170+
assert(rtp.getRank() == 0);
171+
elem = sparse_tensor::genCast(builder, loc, elem, rtp.getElementType());
172+
return builder.create<tensor::FromElementsOp>(loc, rtp, elem);
173+
}
174+
return sparse_tensor::genCast(builder, loc, elem, dstTp);
175+
}
176+
166177
Value sparse_tensor::genIndexLoad(OpBuilder &builder, Location loc, Value mem,
167178
Value s) {
168179
Value load = builder.create<memref::LoadOp>(loc, mem, s);

mlir/lib/Dialect/SparseTensor/Transforms/CodegenUtils.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -142,6 +142,10 @@ class FuncCallOrInlineGenerator {
142142
/// Add type casting between arith and index types when needed.
143143
Value genCast(OpBuilder &builder, Location loc, Value value, Type dstTy);
144144

145+
/// Add conversion from scalar to given type (possibly a 0-rank tensor).
146+
Value genScalarToTensor(OpBuilder &builder, Location loc, Value elem,
147+
Type dstTp);
148+
145149
/// Generates a pointer/index load from the sparse storage scheme. Narrower
146150
/// data types need to be zero extended before casting the value into the
147151
/// index type used for looping and indexing.

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

Lines changed: 0 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -435,19 +435,6 @@ static ReassociationIndices getReassociationForFlattening(ShapedType srcTp) {
435435
return reassociation;
436436
}
437437

438-
/// Generates scalar to tensor cast.
439-
static Value genScalarToTensor(OpBuilder &builder, Location loc, Value elem,
440-
Type dstTp) {
441-
if (auto rtp = dstTp.dyn_cast<RankedTensorType>()) {
442-
// Scalars can only be converted to 0-ranked tensors.
443-
if (rtp.getRank() != 0)
444-
return nullptr;
445-
elem = genCast(builder, loc, elem, rtp.getElementType());
446-
return builder.create<tensor::FromElementsOp>(loc, rtp, elem);
447-
}
448-
return genCast(builder, loc, elem, dstTp);
449-
}
450-
451438
//===----------------------------------------------------------------------===//
452439
// Codegen rules.
453440
//===----------------------------------------------------------------------===//

0 commit comments

Comments
 (0)