Skip to content

[mlir][sparse] Add verification for explicit/implicit value #90111

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 8 commits into from
May 8, 2024
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -502,9 +502,37 @@ def SparseTensorEncodingAttr : SparseTensor_Attr<"SparseTensorEncoding",
//
// Helper function to translate between level/dimension space.
//

SmallVector<int64_t> translateShape(::mlir::ArrayRef<int64_t> srcShape, ::mlir::sparse_tensor::CrdTransDirectionKind) const;
ValueRange translateCrds(::mlir::OpBuilder &builder, ::mlir::Location loc, ::mlir::ValueRange crds, ::mlir::sparse_tensor::CrdTransDirectionKind) const;

//
// COO struct and methods.
//

/// A simple structure that encodes a range of levels in the sparse tensors
/// that forms a COO segment.
struct COOSegment {
std::pair<Level, Level> lvlRange; // [low, high)
bool isSoA;

bool isAoS() const { return !isSoA; }
bool isSegmentStart(Level l) const { return l == lvlRange.first; }
bool inSegment(Level l) const {
return l >= lvlRange.first && l < lvlRange.second;
}
};

/// Returns the starting level of this sparse tensor type for a
/// trailing COO region that spans **at least** two levels. If
/// no such COO region is found, then returns the level-rank.
///
/// DEPRECATED: use getCOOSegment instead;
Level getAoSCOOStart() const;

/// Returns a list of COO segments in the sparse tensor types.
SmallVector<COOSegment> getCOOSegments() const;

//
// Printing methods.
//
Expand Down
25 changes: 4 additions & 21 deletions mlir/include/mlir/Dialect/SparseTensor/IR/SparseTensorType.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,6 @@
namespace mlir {
namespace sparse_tensor {

/// A simple structure that encodes a range of levels in the sparse tensors that
/// forms a COO segment.
struct COOSegment {
std::pair<Level, Level> lvlRange; // [low, high)
bool isSoA;

bool isAoS() const { return !isSoA; }
bool isSegmentStart(Level l) const { return l == lvlRange.first; }
bool inSegment(Level l) const {
return l >= lvlRange.first && l < lvlRange.second;
}
};

//===----------------------------------------------------------------------===//
/// A wrapper around `RankedTensorType`, which has three goals:
///
Expand Down Expand Up @@ -73,12 +60,6 @@ class SparseTensorType {
: SparseTensorType(
RankedTensorType::get(stp.getShape(), stp.getElementType(), enc)) {}

// TODO: remove?
SparseTensorType(SparseTensorEncodingAttr enc)
: SparseTensorType(RankedTensorType::get(
SmallVector<Size>(enc.getDimRank(), ShapedType::kDynamic),
Float32Type::get(enc.getContext()), enc)) {}

SparseTensorType &operator=(const SparseTensorType &) = delete;
SparseTensorType(const SparseTensorType &) = default;

Expand Down Expand Up @@ -369,13 +350,15 @@ class SparseTensorType {
/// no such COO region is found, then returns the level-rank.
///
/// DEPRECATED: use getCOOSegment instead;
Level getAoSCOOStart() const;
Level getAoSCOOStart() const { return getEncoding().getAoSCOOStart(); };

/// Returns [un]ordered COO type for this sparse tensor type.
RankedTensorType getCOOType(bool ordered) const;

/// Returns a list of COO segments in the sparse tensor types.
SmallVector<COOSegment> getCOOSegments() const;
SmallVector<SparseTensorEncodingAttr::COOSegment> getCOOSegments() const {
return getEncoding().getCOOSegments();
}

private:
// These two must be const, to ensure coherence of the memoized fields.
Expand Down
81 changes: 54 additions & 27 deletions mlir/lib/Dialect/SparseTensor/IR/SparseTensorDialect.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,8 @@ void StorageLayout::foreachField(
callback) const {
const auto lvlTypes = enc.getLvlTypes();
const Level lvlRank = enc.getLvlRank();
SmallVector<COOSegment> cooSegs = SparseTensorType(enc).getCOOSegments();
SmallVector<SparseTensorEncodingAttr::COOSegment> cooSegs =
enc.getCOOSegments();
FieldIndex fieldIdx = kDataFieldStartingIdx;

ArrayRef cooSegsRef = cooSegs;
Expand Down Expand Up @@ -211,7 +212,7 @@ StorageLayout::getFieldIndexAndStride(SparseTensorFieldKind kind,
unsigned stride = 1;
if (kind == SparseTensorFieldKind::CrdMemRef) {
assert(lvl.has_value());
const Level cooStart = SparseTensorType(enc).getAoSCOOStart();
const Level cooStart = enc.getAoSCOOStart();
const Level lvlRank = enc.getLvlRank();
if (lvl.value() >= cooStart && lvl.value() < lvlRank) {
lvl = cooStart;
Expand Down Expand Up @@ -912,46 +913,53 @@ LogicalResult SparseTensorEncodingAttr::verifyEncoding(
return emitError()
<< "dimension-rank mismatch between encoding and tensor shape: "
<< getDimRank() << " != " << dimRank;
if (auto expVal = getExplicitVal()) {
Type attrType = llvm::dyn_cast<TypedAttr>(expVal).getType();
if (attrType != elementType) {
return emitError() << "explicit value type mismatch between encoding and "
<< "tensor element type: " << attrType
<< " != " << elementType;
}
}
if (auto impVal = getImplicitVal()) {
Type attrType = llvm::dyn_cast<TypedAttr>(impVal).getType();
if (attrType != elementType) {
return emitError() << "implicit value type mismatch between encoding and "
<< "tensor element type: " << attrType
<< " != " << elementType;
}
// Currently, we only support zero as the implicit value.
auto impFVal = llvm::dyn_cast<FloatAttr>(impVal);
auto impIntVal = llvm::dyn_cast<IntegerAttr>(impVal);
auto impComplexVal = llvm::dyn_cast<complex::NumberAttr>(impVal);
if ((impFVal && impFVal.getValue().isNonZero()) ||
(impIntVal && !impIntVal.getValue().isZero()) ||
(impComplexVal && (impComplexVal.getImag().isNonZero() ||
impComplexVal.getReal().isNonZero()))) {
return emitError() << "implicit value must be zero";
}
}
return success();
}

//===----------------------------------------------------------------------===//
// SparseTensorType Methods.
//===----------------------------------------------------------------------===//

bool mlir::sparse_tensor::SparseTensorType::isCOOType(Level startLvl,
bool isUnique) const {
if (!hasEncoding())
return false;
if (!isCompressedLvl(startLvl) && !isLooseCompressedLvl(startLvl))
return false;
for (Level l = startLvl + 1; l < lvlRank; ++l)
if (!isSingletonLvl(l))
return false;
// If isUnique is true, then make sure that the last level is unique,
// that is, when lvlRank == 1, the only compressed level is unique,
// and when lvlRank > 1, the last singleton is unique.
return !isUnique || isUniqueLvl(lvlRank - 1);
}

Level mlir::sparse_tensor::SparseTensorType::getAoSCOOStart() const {
Level mlir::sparse_tensor::SparseTensorEncodingAttr::getAoSCOOStart() const {
SmallVector<COOSegment> coo = getCOOSegments();
assert(coo.size() == 1 || coo.empty());
if (!coo.empty() && coo.front().isAoS()) {
return coo.front().lvlRange.first;
}
return lvlRank;
return getLvlRank();
}

SmallVector<COOSegment>
mlir::sparse_tensor::SparseTensorType::getCOOSegments() const {
SmallVector<SparseTensorEncodingAttr::COOSegment>
mlir::sparse_tensor::SparseTensorEncodingAttr::getCOOSegments() const {
SmallVector<COOSegment> ret;
if (!hasEncoding() || lvlRank <= 1)
if (getLvlRank() <= 1)
return ret;

ArrayRef<LevelType> lts = getLvlTypes();
Level l = 0;
while (l < lvlRank) {
while (l < getLvlRank()) {
auto lt = lts[l];
if (lt.isa<LevelFormat::Compressed, LevelFormat::LooseCompressed>()) {
auto cur = lts.begin() + l;
Expand All @@ -975,6 +983,25 @@ mlir::sparse_tensor::SparseTensorType::getCOOSegments() const {
return ret;
}

//===----------------------------------------------------------------------===//
// SparseTensorType Methods.
//===----------------------------------------------------------------------===//

bool mlir::sparse_tensor::SparseTensorType::isCOOType(Level startLvl,
bool isUnique) const {
if (!hasEncoding())
return false;
if (!isCompressedLvl(startLvl) && !isLooseCompressedLvl(startLvl))
return false;
for (Level l = startLvl + 1; l < lvlRank; ++l)
if (!isSingletonLvl(l))
return false;
// If isUnique is true, then make sure that the last level is unique,
// that is, when lvlRank == 1, the only compressed level is unique,
// and when lvlRank > 1, the last singleton is unique.
return !isUnique || isUniqueLvl(lvlRank - 1);
}

RankedTensorType
mlir::sparse_tensor::SparseTensorType::getCOOType(bool ordered) const {
SmallVector<LevelType> lvlTypes;
Expand Down
85 changes: 85 additions & 0 deletions mlir/test/Dialect/SparseTensor/invalid_encoding.mlir
Original file line number Diff line number Diff line change
Expand Up @@ -443,3 +443,88 @@ func.func private @NOutOfM(%arg0: tensor<?x?x?xf64, #NOutOfM>) {
func.func private @NOutOfM(%arg0: tensor<?x?x?xf64, #NOutOfM>) {
return
}

// -----

#CSR_ExpType = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
explicitVal = 1 : i32,
implicitVal = 0.0 : f32
}>

// expected-error@+1 {{explicit value type mismatch between encoding and tensor element type: 'i32' != 'f32'}}
func.func private @sparse_csr(tensor<?x?xf32, #CSR_ExpType>)

// -----

#CSR_ImpType = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
explicitVal = 1 : i32,
implicitVal = 0.0 : f32
}>

// expected-error@+1 {{implicit value type mismatch between encoding and tensor element type: 'f32' != 'i32'}}
func.func private @sparse_csr(tensor<?x?xi32, #CSR_ImpType>)

// -----

// expected-error@+1 {{expected a numeric value for explicitVal}}
#CSR_ExpType = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
explicitVal = "str"
}>
func.func private @sparse_csr(tensor<?x?xi32, #CSR_ExpType>)

// -----

// expected-error@+1 {{expected a numeric value for implicitVal}}
#CSR_ImpType = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
implicitVal = "str"
}>
func.func private @sparse_csr(tensor<?x?xi32, #CSR_ImpType>)

// -----

#CSR_ImpVal = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
implicitVal = 1 : i32
}>

// expected-error@+1 {{implicit value must be zero}}
func.func private @sparse_csr(tensor<?x?xi32, #CSR_ImpVal>)

// -----

#CSR_ImpVal = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 32,
crdWidth = 32,
implicitVal = 1.0 : f32
}>

// expected-error@+1 {{implicit value must be zero}}
func.func private @sparse_csr(tensor<?x?xf32, #CSR_ImpVal>)

// -----

#CSR_OnlyOnes = #sparse_tensor.encoding<{
map = (d0, d1) -> (d0 : dense, d1 : compressed),
posWidth = 64,
crdWidth = 64,
explicitVal = #complex.number<:f32 1.0, 0.0>,
implicitVal = #complex.number<:f32 1.0, 0.0>
}>

// expected-error@+1 {{implicit value must be zero}}
func.func private @sparse_csr(tensor<?x?xcomplex<f32>, #CSR_OnlyOnes>)
Loading