/// Generates a constant of `index` type.
inline static Value constantIndex(ConversionPatternRewriter &rewriter,
- Location loc, unsigned i) {
+ Location loc, int64_t i) {
return rewriter.create<arith::ConstantIndexOp>(loc, i);
}
return result;
}
+/// Generates dimension size call.
+static Value genDimSizeCall(ConversionPatternRewriter &rewriter, Operation *op,
+ SparseTensorEncodingAttr &enc, Value src,
+ int64_t idx) {
+ // Permute the index according to an optional dimension ordering.
+ if (AffineMap p = enc.getDimOrdering())
+ idx = p.getPermutedPosition(idx);
+ // Generate the call.
+ Location loc = op->getLoc();
+ StringRef name = "sparseDimSize";
+ SmallVector<Value, 2> params;
+ params.push_back(src);
+ params.push_back(constantIndex(rewriter, loc, idx));
+ Type iTp = rewriter.getIndexType();
+ auto fn = getFunc(op, name, iTp, params);
+ return rewriter.create<CallOp>(loc, iTp, fn, params).getResult(0);
+}
+
+/// Generates a call into the "swiss army knife" method of the sparse runtime
+/// support library for materializing sparse tensors into the computation.
+static Value genNewCall(ConversionPatternRewriter &rewriter, Operation *op,
+ ArrayRef<Value> params) {
+ Location loc = op->getLoc();
+ StringRef name = "newSparseTensor";
+ Type pTp = LLVM::LLVMPointerType::get(rewriter.getI8Type());
+ auto fn = getFunc(op, name, pTp, params, /*emitCInterface=*/true);
+ auto call = rewriter.create<CallOp>(loc, pTp, fn, params);
+ return call.getResult(0);
+}
+
+/// Populates given sizes array from type.
+static void sizesFromType(ConversionPatternRewriter &rewriter,
+ SmallVector<Value, 4> &sizes, Location loc,
+ ShapedType stp) {
+ auto shape = stp.getShape();
+ for (unsigned i = 0, rank = stp.getRank(); i < rank; i++) {
+ uint64_t s = shape[i] == ShapedType::kDynamicSize ? 0 : shape[i];
+ sizes.push_back(constantIndex(rewriter, loc, s));
+ }
+}
+
+/// Populates given sizes array from source.
+static void sizesFromSrc(ConversionPatternRewriter &rewriter,
+ SmallVector<Value, 4> &sizes, Location loc,
+ Value src) {
+ ShapedType stp = src.getType().cast<ShapedType>();
+ for (unsigned i = 0, rank = stp.getRank(); i < rank; i++)
+ sizes.push_back(linalg::createOrFoldDimOp(rewriter, loc, src, i));
+}
+
+/// Populates given sizes array from type (for static sizes) and from
+/// an already converted into opague pointer source (for dynamic sizes).
+static void sizesFromPtr(ConversionPatternRewriter &rewriter,
+ SmallVector<Value, 4> &sizes, Operation *op,
+ SparseTensorEncodingAttr &enc, ShapedType stp,
+ Value src) {
+ auto shape = stp.getShape();
+ for (unsigned i = 0, rank = stp.getRank(); i < rank; i++)
+ if (shape[i] == ShapedType::kDynamicSize)
+ sizes.push_back(genDimSizeCall(rewriter, op, enc, src, i));
+ else
+ sizes.push_back(constantIndex(rewriter, op->getLoc(), shape[i]));
+}
+
/// Generates a temporary buffer of the given size and type.
static Value genAlloca(ConversionPatternRewriter &rewriter, Location loc,
unsigned sz, Type tp) {
return rewriter.create<memref::AllocaOp>(loc, memTp, ValueRange{a});
}
-/// Fills a temporary buffer of the given type with arguments.
+/// Generates a temporary buffer of the given type and given contents.
static Value genBuffer(ConversionPatternRewriter &rewriter, Location loc,
ArrayRef<Value> values) {
unsigned sz = values.size();
return buffer;
}
-/// Generates a call into the "swiss army knife" method of the sparse runtime
-/// support library for materializing sparse tensors into the computation. The
-/// method returns the call value and assigns the permutation to 'perm'.
-static Value genNewCall(ConversionPatternRewriter &rewriter, Operation *op,
- SparseTensorEncodingAttr &enc, uint32_t action,
- Value &perm, ValueRange szs, Value ptr = Value()) {
+/// Populates parameters required to call the "swiss army knife" method of the
+/// sparse runtime support library for materializing sparse tensors into the
+/// computation.
+static void newParams(ConversionPatternRewriter &rewriter,
+ SmallVector<Value, 8> ¶ms, Operation *op,
+ SparseTensorEncodingAttr &enc, uint32_t action,
+ ValueRange szs, Value ptr = Value()) {
Location loc = op->getLoc();
- ShapedType resType = op->getResult(0).getType().cast<ShapedType>();
- SmallVector<Value, 8> params;
- // Sparsity annotations in tensor constant form.
- SmallVector<Value, 4> attrs;
ArrayRef<SparseTensorEncodingAttr::DimLevelType> dlt = enc.getDimLevelType();
unsigned sz = dlt.size();
+ // Sparsity annotations.
+ SmallVector<Value, 4> attrs;
for (unsigned i = 0; i < sz; i++)
attrs.push_back(constantI8(rewriter, loc, getDimLevelTypeEncoding(dlt[i])));
params.push_back(genBuffer(rewriter, loc, attrs));
- // Dimension sizes array of the enveloping *dense* tensor. Useful for either
+ // Dimension sizes array of the enveloping tensor. Useful for either
// verification of external data, or for construction of internal data.
- auto shape = resType.getShape();
+ // The index type is casted to I64 for API consistency.
+ Type iTp = rewriter.getI64Type();
SmallVector<Value, 4> sizes;
- if (szs.size() > 0) {
- for (Value s : szs)
- sizes.push_back(
- rewriter.create<arith::IndexCastOp>(loc, s, rewriter.getI64Type()));
- } else {
- for (unsigned i = 0; i < sz; i++) {
- uint64_t s = shape[i] == ShapedType::kDynamicSize ? 0 : shape[i];
- sizes.push_back(constantI64(rewriter, loc, s));
- }
- }
+ for (Value s : szs)
+ sizes.push_back(rewriter.create<arith::IndexCastOp>(loc, s, iTp));
params.push_back(genBuffer(rewriter, loc, sizes));
// Dimension order permutation array. This is the "identity" permutation by
// default, or otherwise the "reverse" permutation of a given ordering, so
for (unsigned i = 0; i < sz; i++)
rev[i] = constantI64(rewriter, loc, i);
}
- perm = genBuffer(rewriter, loc, rev);
- params.push_back(perm);
+ params.push_back(genBuffer(rewriter, loc, rev));
// Secondary and primary types encoding.
+ ShapedType resType = op->getResult(0).getType().cast<ShapedType>();
unsigned secPtr = getOverheadTypeEncoding(enc.getPointerBitWidth());
unsigned secInd = getOverheadTypeEncoding(enc.getIndexBitWidth());
unsigned primary = getPrimaryTypeEncoding(resType.getElementType());
ptr = rewriter.create<LLVM::NullOp>(loc, pTp);
params.push_back(constantI32(rewriter, loc, action));
params.push_back(ptr);
- // Generate the call to create new tensor.
- StringRef name = "newSparseTensor";
- auto call = rewriter.create<CallOp>(
- loc, pTp, getFunc(op, name, pTp, params, /*emitCInterface=*/true),
- params);
- return call.getResult(0);
}
/// Generates the comparison `v != 0` where `v` is of numeric type `t`.
params.push_back(ind);
params.push_back(perm);
Type pTp = LLVM::LLVMPointerType::get(rewriter.getI8Type());
- rewriter.create<CallOp>(
- loc, pTp, getFunc(op, name, pTp, params, /*emitCInterface=*/true),
- params);
+ auto fn = getFunc(op, name, pTp, params, /*emitCInterface=*/true);
+ rewriter.create<CallOp>(loc, pTp, fn, params);
}
/// If the tensor is a sparse constant, generates and returns the pair of
LogicalResult
matchAndRewrite(tensor::DimOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
- Type resType = op.getType();
+ // Only rewrite annotated DimOp with constant index.
auto enc = getSparseTensorEncoding(op.source().getType());
if (!enc)
return failure();
- // Permute the dim index.
Optional<int64_t> index = op.getConstantIndex();
if (!index.hasValue())
return failure();
- int64_t idx = index.getValue();
- if (AffineMap p = enc.getDimOrdering())
- idx = p.getPermutedPosition(idx);
// Generate the call.
- StringRef name = "sparseDimSize";
- SmallVector<Value, 2> params;
- params.push_back(adaptor.getOperands()[0]);
- params.push_back(constantIndex(rewriter, op.getLoc(), idx));
- rewriter.replaceOpWithNewOp<CallOp>(
- op, resType, getFunc(op, name, resType, params), params);
+ Value src = adaptor.getOperands()[0];
+ int64_t idx = index.getValue();
+ rewriter.replaceOp(op, genDimSizeCall(rewriter, op, enc, src, idx));
return success();
}
};
auto enc = getSparseTensorEncoding(resType);
if (!enc)
return failure();
- Value perm;
- rewriter.replaceOp(op, genNewCall(rewriter, op, enc, kFromFile, perm, {},
- adaptor.getOperands()[0]));
+ // Generate the call to construct tensor from ptr. The sizes are
+ // inferred from the result type of the new operator.
+ SmallVector<Value, 4> sizes;
+ SmallVector<Value, 8> params;
+ sizesFromType(rewriter, sizes, op.getLoc(), resType.cast<ShapedType>());
+ Value ptr = adaptor.getOperands()[0];
+ newParams(rewriter, params, op, enc, kFromFile, sizes, ptr);
+ rewriter.replaceOp(op, genNewCall(rewriter, op, params));
return success();
}
};
auto enc = getSparseTensorEncoding(resType);
if (!enc)
return failure();
- Value perm;
- rewriter.replaceOp(
- op, genNewCall(rewriter, op, enc, kEmpty, perm, adaptor.getOperands()));
+ // Generate the call to construct empty tensor. The sizes are
+ // explicitly defined by the arguments to the init operator.
+ SmallVector<Value, 8> params;
+ newParams(rewriter, params, op, enc, kEmpty, adaptor.getOperands());
+ rewriter.replaceOp(op, genNewCall(rewriter, op, params));
return success();
}
};
LogicalResult
matchAndRewrite(ConvertOp op, OpAdaptor adaptor,
ConversionPatternRewriter &rewriter) const override {
+ Location loc = op->getLoc();
Type resType = op.getType();
+ Type srcType = op.source().getType();
auto encDst = getSparseTensorEncoding(resType);
- auto encSrc = getSparseTensorEncoding(op.source().getType());
- auto src = adaptor.getOperands()[0];
+ auto encSrc = getSparseTensorEncoding(srcType);
+ Value src = adaptor.getOperands()[0];
if (encDst && encSrc) {
// This is a sparse => sparse conversion, which is handled as follows:
// t = src->toCOO(); ; src to COO in dst order
// Using the coordinate scheme as an intermediate does not always
// yield the fastest conversion but avoids the need for a full
// O(N^2) conversion matrix.
- Value perm;
- Value coo = genNewCall(rewriter, op, encDst, kToCOO, perm, {}, src);
- rewriter.replaceOp(
- op, genNewCall(rewriter, op, encDst, kFromCOO, perm, {}, coo));
+ SmallVector<Value, 4> sizes;
+ SmallVector<Value, 8> params;
+ sizesFromPtr(rewriter, sizes, op, encSrc, srcType.cast<ShapedType>(),
+ src);
+ newParams(rewriter, params, op, encDst, kToCOO, sizes, src);
+ Value coo = genNewCall(rewriter, op, params);
+ params[6] = constantI32(rewriter, loc, kFromCOO);
+ params[7] = coo;
+ rewriter.replaceOp(op, genNewCall(rewriter, op, params));
return success();
}
if (!encDst || encSrc) {
// Also note that the code below only generates the "new" ops and
// the loop-nest per se; whereas the entire body of the innermost
// loop is generated by genAddElt().
- Location loc = op->getLoc();
- ShapedType shape = resType.cast<ShapedType>();
- Value perm;
- Value ptr = genNewCall(rewriter, op, encDst, kEmptyCOO, perm, {});
- Value ind =
- genAlloca(rewriter, loc, shape.getRank(), rewriter.getIndexType());
+ ShapedType stp = resType.cast<ShapedType>();
+ unsigned rank = stp.getRank();
+ SmallVector<Value, 4> sizes;
+ SmallVector<Value, 8> params;
+ sizesFromSrc(rewriter, sizes, loc, src);
+ newParams(rewriter, params, op, encDst, kEmptyCOO, sizes);
+ Value ptr = genNewCall(rewriter, op, params);
+ Value ind = genAlloca(rewriter, loc, rank, rewriter.getIndexType());
+ Value perm = params[2];
SmallVector<Value> lo;
SmallVector<Value> hi;
SmallVector<Value> st;
hi.push_back(linalg::createOrFoldDimOp(rewriter, loc, values, 0));
st.push_back(one);
} else {
- for (unsigned i = 0, rank = shape.getRank(); i < rank; i++) {
+ for (unsigned i = 0; i < rank; i++) {
lo.push_back(zero);
hi.push_back(linalg::createOrFoldDimOp(rewriter, loc, src, i));
st.push_back(one);
}
}
- Type eltType = shape.getElementType();
- unsigned rank = shape.getRank();
+ Type eltType = stp.getElementType();
scf::buildLoopNest(
rewriter, op.getLoc(), lo, hi, st, {},
[&](OpBuilder &builder, Location loc, ValueRange ivs,
genAddEltCall(rewriter, op, eltType, ptr, val, ind, perm);
return {};
});
- rewriter.replaceOp(
- op, genNewCall(rewriter, op, encDst, kFromCOO, perm, {}, ptr));
+ // Final call to construct sparse tensor storage.
+ params[6] = constantI32(rewriter, loc, kFromCOO);
+ params[7] = ptr;
+ rewriter.replaceOp(op, genNewCall(rewriter, op, params));
return success();
}
};
ConversionPatternRewriter &rewriter) const override {
StringRef name = "delSparseTensor";
TypeRange none;
- rewriter.create<CallOp>(op.getLoc(), none,
- getFunc(op, name, none, adaptor.getOperands()),
- adaptor.getOperands());
+ auto fn = getFunc(op, name, none, adaptor.getOperands());
+ rewriter.create<CallOp>(op.getLoc(), none, fn, adaptor.getOperands());
rewriter.eraseOp(op);
return success();
}
name = "sparsePointers8";
else
return failure();
- rewriter.replaceOpWithNewOp<CallOp>(op, resType,
- getFunc(op, name, resType,
- adaptor.getOperands(),
- /*emitCInterface=*/true),
- adaptor.getOperands());
+ auto fn = getFunc(op, name, resType, adaptor.getOperands(),
+ /*emitCInterface=*/true);
+ rewriter.replaceOpWithNewOp<CallOp>(op, resType, fn, adaptor.getOperands());
return success();
}
};
name = "sparseIndices8";
else
return failure();
- rewriter.replaceOpWithNewOp<CallOp>(op, resType,
- getFunc(op, name, resType,
- adaptor.getOperands(),
- /*emitCInterface=*/true),
- adaptor.getOperands());
+ auto fn = getFunc(op, name, resType, adaptor.getOperands(),
+ /*emitCInterface=*/true);
+ rewriter.replaceOpWithNewOp<CallOp>(op, resType, fn, adaptor.getOperands());
return success();
}
};
name = "sparseValuesI8";
else
return failure();
- rewriter.replaceOpWithNewOp<CallOp>(op, resType,
- getFunc(op, name, resType,
- adaptor.getOperands(),
- /*emitCInterface=*/true),
- adaptor.getOperands());
+ auto fn = getFunc(op, name, resType, adaptor.getOperands(),
+ /*emitCInterface=*/true);
+ rewriter.replaceOpWithNewOp<CallOp>(op, resType, fn, adaptor.getOperands());
return success();
}
};
// CHECK-DAG: %[[JJ:.*]] = arith.index_cast %[[J]] : index to i64
// CHECK-DAG: memref.store %[[II]], %[[Q]][%[[C0]]] : memref<2xi64>
// CHECK-DAG: memref.store %[[JJ]], %[[Q]][%[[C1]]] : memref<2xi64>
-// CHECK: %[[A:.*]] = llvm.mlir.null : !llvm.ptr<i8>
-// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[A]])
+// CHECK: %[[NP:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[NP]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_init(%arg0: index, %arg1: index) -> tensor<?x?xf64, #SparseMatrix> {
%0 = sparse_tensor.init [%arg0, %arg1] : tensor<?x?xf64, #SparseMatrix>
// CHECK-SAME: %[[A:.*]]: tensor<?xi32>) -> !llvm.ptr<i8>
// CHECK-DAG: %[[C0:.*]] = arith.constant 0 : index
// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : index
+// CHECK-DAG: %[[U:.*]] = tensor.dim %[[A]], %[[C0]] : tensor<?xi32>
// CHECK-DAG: %[[P:.*]] = memref.alloca() : memref<1xi8>
// CHECK-DAG: %[[Q:.*]] = memref.alloca() : memref<1xi64>
// CHECK-DAG: %[[R:.*]] = memref.alloca() : memref<1xi64>
// CHECK-DAG: %[[X:.*]] = memref.cast %[[P]] : memref<1xi8> to memref<?xi8>
// CHECK-DAG: %[[Y:.*]] = memref.cast %[[Q]] : memref<1xi64> to memref<?xi64>
// CHECK-DAG: %[[Z:.*]] = memref.cast %[[R]] : memref<1xi64> to memref<?xi64>
-// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.}})
+// CHECK: %[[NP:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[NP]])
// CHECK: %[[M:.*]] = memref.alloca() : memref<1xindex>
// CHECK: %[[T:.*]] = memref.cast %[[M]] : memref<1xindex> to memref<?xindex>
-// CHECK: %[[U:.*]] = tensor.dim %[[A]], %[[C0]] : tensor<?xi32>
// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[U]] step %[[C1]] {
// CHECK: %[[E:.*]] = tensor.extract %[[A]][%[[I]]] : tensor<?xi32>
// CHECK: memref.store %[[I]], %[[M]][%[[C0]]] : memref<1xindex>
// CHECK: call @addEltI32(%[[C]], %[[E]], %[[T]], %[[Z]])
// CHECK: }
-// CHECK: %[[T:.*]] = call @newSparseTensor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_convert_1d(%arg0: tensor<?xi32>) -> tensor<?xi32, #SparseVector> {
%0 = sparse_tensor.convert %arg0 : tensor<?xi32> to tensor<?xi32, #SparseVector>
// CHECK-LABEL: func @sparse_convert_1d_ss(
// CHECK-SAME: %[[A:.*]]: !llvm.ptr<i8>)
-// CHECK: %[[C:.*]] = call @newSparseTensor(%{{.}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[A]])
-// CHECK: %[[T:.*]] = call @newSparseTensor(%{{.}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
+// CHECK-DAG: %[[P:.*]] = memref.alloca() : memref<1xi8>
+// CHECK-DAG: %[[Q:.*]] = memref.alloca() : memref<1xi64>
+// CHECK-DAG: %[[R:.*]] = memref.alloca() : memref<1xi64>
+// CHECK-DAG: %[[X:.*]] = memref.cast %[[P]] : memref<1xi8> to memref<?xi8>
+// CHECK-DAG: %[[Y:.*]] = memref.cast %[[Q]] : memref<1xi64> to memref<?xi64>
+// CHECK-DAG: %[[Z:.*]] = memref.cast %[[R]] : memref<1xi64> to memref<?xi64>
+// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[A]])
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_convert_1d_ss(%arg0: tensor<?xf32, #SparseVector64>) -> tensor<?xf32, #SparseVector32> {
%0 = sparse_tensor.convert %arg0 : tensor<?xf32, #SparseVector64> to tensor<?xf32, #SparseVector32>
// CHECK-DAG: %[[X:.*]] = memref.cast %[[P]] : memref<2xi8> to memref<?xi8>
// CHECK-DAG: %[[Y:.*]] = memref.cast %[[Q]] : memref<2xi64> to memref<?xi64>
// CHECK-DAG: %[[Z:.*]] = memref.cast %[[R]] : memref<2xi64> to memref<?xi64>
-// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.}})
+// CHECK: %[[NP:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[NP]])
// CHECK: %[[M:.*]] = memref.alloca() : memref<2xindex>
// CHECK: %[[T:.*]] = memref.cast %[[M]] : memref<2xindex> to memref<?xindex>
// CHECK: scf.for %[[I:.*]] = %[[C0]] to %{{.*}} step %[[C1]] {
// CHECK: call @addEltF64(%[[C]], %[[E]], %[[T]], %[[Z]])
// CHECK: }
// CHECK: }
-// CHECK: %[[T:.*]] = call @newSparseTensor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_convert_2d(%arg0: tensor<2x4xf64>) -> tensor<2x4xf64, #SparseMatrix> {
%0 = sparse_tensor.convert %arg0 : tensor<2x4xf64> to tensor<2x4xf64, #SparseMatrix>
// CHECK-DAG: %[[X:.*]] = memref.cast %[[P]] : memref<2xi8> to memref<?xi8>
// CHECK-DAG: %[[Y:.*]] = memref.cast %[[Q]] : memref<2xi64> to memref<?xi64>
// CHECK-DAG: %[[Z:.*]] = memref.cast %[[R]] : memref<2xi64> to memref<?xi64>
-// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.}})
+// CHECK: %[[NP:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[NP]])
// CHECK: %[[M:.*]] = memref.alloca() : memref<2xindex>
// CHECK: %[[N:.*]] = memref.cast %[[M]] : memref<2xindex> to memref<?xindex>
// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[C2]] step %[[C1]] {
// CHECK: %[[V:.*]] = tensor.extract %{{.*}}[%[[I]]] : tensor<2xf32>
// CHECK: call @addEltF32(%{{.*}}, %[[V]], %[[N]], %{{.*}})
// CHECK: }
-// CHECK: %[[T:.*]] = call @newSparseTensor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_constant() -> tensor<8x7xf32, #SparseMatrix>{
// Initialize a tensor.
// CHECK-DAG: %[[C0:.*]] = arith.constant 0 : index
// CHECK-DAG: %[[C1:.*]] = arith.constant 1 : index
// CHECK-DAG: %[[C2:.*]] = arith.constant 2 : index
+// CHECK-DAG: %[[U1:.*]] = tensor.dim %[[A]], %[[C0]] : tensor<?x?x?xf64>
+// CHECK-DAG: %[[U2:.*]] = tensor.dim %[[A]], %[[C1]] : tensor<?x?x?xf64>
+// CHECK-DAG: %[[U3:.*]] = tensor.dim %[[A]], %[[C2]] : tensor<?x?x?xf64>
// CHECK-DAG: %[[P:.*]] = memref.alloca() : memref<3xi8>
// CHECK-DAG: %[[Q:.*]] = memref.alloca() : memref<3xi64>
// CHECK-DAG: %[[R:.*]] = memref.alloca() : memref<3xi64>
// CHECK-DAG: %[[X:.*]] = memref.cast %[[P]] : memref<3xi8> to memref<?xi8>
// CHECK-DAG: %[[Y:.*]] = memref.cast %[[Q]] : memref<3xi64> to memref<?xi64>
// CHECK-DAG: %[[Z:.*]] = memref.cast %[[R]] : memref<3xi64> to memref<?xi64>
-// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.}})
+// CHECK: %[[NP:.*]] = llvm.mlir.null : !llvm.ptr<i8>
+// CHECK: %[[C:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[NP]])
// CHECK: %[[M:.*]] = memref.alloca() : memref<3xindex>
// CHECK: %[[N:.*]] = memref.cast %[[M]] : memref<3xindex> to memref<?xindex>
-// CHECK: %[[U1:.*]] = tensor.dim %[[A]], %[[C0]] : tensor<?x?x?xf64>
-// CHECK: %[[U2:.*]] = tensor.dim %[[A]], %[[C1]] : tensor<?x?x?xf64>
-// CHECK: %[[U3:.*]] = tensor.dim %[[A]], %[[C2]] : tensor<?x?x?xf64>
// CHECK: scf.for %[[I:.*]] = %[[C0]] to %[[U1]] step %[[C1]] {
// CHECK: scf.for %[[J:.*]] = %[[C0]] to %[[U2]] step %[[C1]] {
// CHECK: scf.for %[[K:.*]] = %[[C0]] to %[[U3]] step %[[C1]] {
// CHECK: }
// CHECK: }
// CHECK: }
-// CHECK: %[[T:.*]] = call @newSparseTensor(%{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
+// CHECK: %[[T:.*]] = call @newSparseTensor(%[[X]], %[[Y]], %[[Z]], %{{.*}}, %{{.*}}, %{{.*}}, %{{.*}}, %[[C]])
// CHECK: return %[[T]] : !llvm.ptr<i8>
func @sparse_convert_3d(%arg0: tensor<?x?x?xf64>) -> tensor<?x?x?xf64, #SparseTensor> {
%0 = sparse_tensor.convert %arg0 : tensor<?x?x?xf64> to tensor<?x?x?xf64, #SparseTensor>
--- /dev/null
+// RUN: mlir-opt %s \
+// RUN: --sparsification --sparse-tensor-conversion \
+// RUN: --linalg-bufferize --convert-linalg-to-loops \
+// RUN: --convert-vector-to-scf --convert-scf-to-std \
+// RUN: --func-bufferize --tensor-constant-bufferize --tensor-bufferize \
+// RUN: --std-bufferize --finalizing-bufferize --lower-affine \
+// RUN: --convert-vector-to-llvm --convert-memref-to-llvm --convert-math-to-llvm \
+// RUN: --convert-std-to-llvm --reconcile-unrealized-casts | \
+// RUN: mlir-cpu-runner \
+// RUN: -e entry -entry-point-result=void \
+// RUN: -shared-libs=%mlir_integration_test_dir/libmlir_c_runner_utils%shlibext | \
+// RUN: FileCheck %s
+
+#DCSR = #sparse_tensor.encoding<{
+ dimLevelType = [ "compressed", "compressed" ]
+}>
+
+#DCSC = #sparse_tensor.encoding<{
+ dimLevelType = [ "compressed", "compressed" ],
+ dimOrdering = affine_map<(i,j) -> (j,i)>
+}>
+
+//
+// Integration test that tests conversions between sparse tensors,
+// where the dynamic sizes of the shape of the enveloping tensor
+// may change (the actual underlying sizes obviously never change).
+//
+module {
+
+ //
+ // Helper method to print values array. The transfer actually
+ // reads more than required to verify size of buffer as well.
+ //
+ func @dump(%arg0: memref<?xf64>) {
+ %c = arith.constant 0 : index
+ %d = arith.constant -1.0 : f64
+ %0 = vector.transfer_read %arg0[%c], %d: memref<?xf64>, vector<8xf64>
+ vector.print %0 : vector<8xf64>
+ return
+ }
+
+ func @entry() {
+ %t1 = arith.constant sparse<
+ [ [0,0], [0,1], [0,63], [1,0], [1,1], [31,0], [31,63] ],
+ [ 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0 ]> : tensor<32x64xf64>
+ %t2 = tensor.cast %t1 : tensor<32x64xf64> to tensor<?x?xf64>
+
+ // Four dense to sparse conversions.
+ %1 = sparse_tensor.convert %t1 : tensor<32x64xf64> to tensor<?x?xf64, #DCSR>
+ %2 = sparse_tensor.convert %t1 : tensor<32x64xf64> to tensor<?x?xf64, #DCSC>
+ %3 = sparse_tensor.convert %t2 : tensor<?x?xf64> to tensor<?x?xf64, #DCSR>
+ %4 = sparse_tensor.convert %t2 : tensor<?x?xf64> to tensor<?x?xf64, #DCSC>
+
+ // Two cross conversions.
+ %5 = sparse_tensor.convert %3 : tensor<?x?xf64, #DCSR> to tensor<?x?xf64, #DCSC>
+ %6 = sparse_tensor.convert %4 : tensor<?x?xf64, #DCSC> to tensor<?x?xf64, #DCSR>
+
+ //
+ // All proper row-/column-wise?
+ //
+ // CHECK: ( 1, 2, 3, 4, 5, 6, 7, -1 )
+ // CHECK: ( 1, 4, 6, 2, 5, 3, 7, -1 )
+ // CHECK: ( 1, 2, 3, 4, 5, 6, 7, -1 )
+ // CHECK: ( 1, 4, 6, 2, 5, 3, 7, -1 )
+ // CHECK: ( 1, 4, 6, 2, 5, 3, 7, -1 )
+ // CHECK: ( 1, 2, 3, 4, 5, 6, 7, -1 )
+ //
+ %m1 = sparse_tensor.values %1 : tensor<?x?xf64, #DCSR> to memref<?xf64>
+ %m2 = sparse_tensor.values %2 : tensor<?x?xf64, #DCSC> to memref<?xf64>
+ %m3 = sparse_tensor.values %3 : tensor<?x?xf64, #DCSR> to memref<?xf64>
+ %m4 = sparse_tensor.values %4 : tensor<?x?xf64, #DCSC> to memref<?xf64>
+ %m5 = sparse_tensor.values %5 : tensor<?x?xf64, #DCSC> to memref<?xf64>
+ %m6 = sparse_tensor.values %6 : tensor<?x?xf64, #DCSR> to memref<?xf64>
+ call @dump(%m1) : (memref<?xf64>) -> ()
+ call @dump(%m2) : (memref<?xf64>) -> ()
+ call @dump(%m3) : (memref<?xf64>) -> ()
+ call @dump(%m4) : (memref<?xf64>) -> ()
+ call @dump(%m5) : (memref<?xf64>) -> ()
+ call @dump(%m6) : (memref<?xf64>) -> ()
+
+ // Release the resources.
+ sparse_tensor.release %1 : tensor<?x?xf64, #DCSR>
+ sparse_tensor.release %2 : tensor<?x?xf64, #DCSC>
+ sparse_tensor.release %3 : tensor<?x?xf64, #DCSR>
+ sparse_tensor.release %4 : tensor<?x?xf64, #DCSC>
+ sparse_tensor.release %5 : tensor<?x?xf64, #DCSC>
+ sparse_tensor.release %6 : tensor<?x?xf64, #DCSR>
+
+ return
+ }
+}