diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorConversion.cpp b/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorConversion.cpp --- a/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorConversion.cpp +++ b/mlir/lib/Dialect/SparseTensor/Transforms/SparseTensorConversion.cpp @@ -238,7 +238,7 @@ /// the following and the insertion point after this routine is inside the /// if-then branch behind the assignment to ind. This is to ensure that the /// addEltX call generated after is inside the if-then branch. -/// if (tensor[ivs]!=0) { +/// if (tensor[ivs] != 0) /// ind = ivs static Value genIndexAndValueForDense(OpBuilder &builder, Location loc, Value tensor, Value ind, ValueRange ivs) { @@ -382,6 +382,133 @@ return true; } +/// Helper method to translate indices during a reshaping operation. +/// TODO: provide as general utility to MLIR at large? +static void translateIndices(Location loc, ConversionPatternRewriter &rewriter, + ArrayRef reassociation, + TensorType dstTp, TensorType srcTp, Value dstIdx, + Value srcIdx) { + unsigned dstRank = dstTp.getRank(); + unsigned srcRank = srcTp.getRank(); + unsigned start = 0; + unsigned i = 0; + bool isExpand = srcRank > dstRank; + ArrayRef shape = isExpand ? srcTp.getShape() : dstTp.getShape(); + // Iterate over reassociation map. + for (const auto &map : llvm::enumerate(reassociation)) { + // Prepare strides information in dimension slice. + uint64_t linear = 1; + for (unsigned j = start, end = start + map.value().size(); j < end; j++) { + assert(!ShapedType::isDynamic(shape[j])); + linear *= shape[j]; + } + // Start collapse. + Value idx = constantIndex(rewriter, loc, i++); + Value val; + if (!isExpand) + val = rewriter.create(loc, srcIdx, idx); + // Iterate over dimension slice. + for (unsigned j = start, end = start + map.value().size(); j < end; j++) { + linear /= shape[j]; + Value stride = constantIndex(rewriter, loc, linear); + Value jdx = constantIndex(rewriter, loc, j); + if (isExpand) { + Value old = rewriter.create(loc, srcIdx, jdx); + Value mul = linear == 1 + ? old + : rewriter.create(loc, old, stride); + val = val ? rewriter.create(loc, val, mul) : mul; + } else { + Value old = val; + if (linear != 1) + val = rewriter.create(loc, val, stride); + rewriter.create(loc, val, dstIdx, jdx); + if (linear != 1) + val = rewriter.create(loc, old, stride); + } + } + // Finalize expansion. + if (isExpand) + rewriter.create(loc, val, dstIdx, idx); + start += map.value().size(); + } + // Sanity. + assert((isExpand && i == dstRank) || (!isExpand && i == srcRank)); +} + +/// Generate code for a general sparse to sparse reshaping operation. +/// Note that unlike dense reshaping (which can be done with a "cheap" +/// change of view), sparse reshaping is currently done with actual +/// data shuffling. +/// +/// TODO: proportional to nnz, but still a lot of data movement! improve! +/// +/// iter = src->toCOO(); +/// coo = newSparseCOO() +/// while (elem = iter->getNext()) { +/// reshape(elem.indices) +/// elem.value +/// } +/// s = newSparseTensor(coo) +static LogicalResult +genSparse2SparseReshape(Operation *op, ConversionPatternRewriter &rewriter, + ArrayRef reassociation, Value src, + RankedTensorType dstTp, RankedTensorType srcTp) { + Location loc = op->getLoc(); + auto encDst = getSparseTensorEncoding(dstTp); + auto encSrc = getSparseTensorEncoding(srcTp); + assert(encDst && encSrc); + unsigned srcRank = srcTp.getRank(); + unsigned dstRank = dstTp.getRank(); + Type elemTp = srcTp.getElementType(); + assert(elemTp == dstTp.getElementType() && + "reshape should not change element type"); + // Start an iterator over the source tensor (in original index order). + auto noPerm = SparseTensorEncodingAttr::get( + op->getContext(), encSrc.getDimLevelType(), AffineMap(), + encSrc.getPointerBitWidth(), encSrc.getIndexBitWidth()); + SmallVector sizes; + SmallVector params; + sizesFromPtr(rewriter, sizes, op, noPerm, srcTp, src); + newParams(rewriter, params, op, srcTp, noPerm, Action::kToIterator, sizes, + src); + Value iter = genNewCall(rewriter, op, params); + // Start a new COO for the destination tensor. + sizes.clear(); + params.clear(); + sizesFromPtr(rewriter, sizes, op, encDst, dstTp, src); + newParams(rewriter, params, op, dstTp, encDst, Action::kEmptyCOO, sizes); + Value coo = genNewCall(rewriter, op, params); + Value dstPerm = params[2]; + // Construct a while loop over the iterator. + Value srcIdx = genAlloca(rewriter, loc, srcRank, rewriter.getIndexType()); + Value dstIdx = genAlloca(rewriter, loc, dstRank, rewriter.getIndexType()); + Value elemPtr = genAllocaScalar(rewriter, loc, elemTp); + SmallVector noArgs; + SmallVector noTypes; + auto whileOp = rewriter.create(loc, noTypes, noArgs); + Block *before = rewriter.createBlock(&whileOp.getBefore(), {}, noTypes); + rewriter.setInsertionPointToEnd(before); + Value cond = genGetNextCall(rewriter, op, iter, srcIdx, elemPtr); + rewriter.create(loc, cond, before->getArguments()); + // Translate indices from source to target and insert. Note that we + // do not need to store the value in elemPtr, as is still there. + Block *after = rewriter.createBlock(&whileOp.getAfter(), {}, noTypes); + rewriter.setInsertionPointToStart(after); + translateIndices(loc, rewriter, reassociation, dstTp, srcTp, dstIdx, srcIdx); + genAddEltCall(rewriter, op, elemTp, coo, elemPtr, dstIdx, dstPerm); + rewriter.create(loc); + // Final call to construct sparse tensor storage and free temporary resources. + rewriter.setInsertionPointAfter(whileOp); + params[6] = constantAction(rewriter, loc, Action::kFromCOO); + params[7] = coo; + Value dst = genNewCall(rewriter, op, params); + genDelCOOCall(rewriter, op, elemTp, coo); + genDelCOOCall(rewriter, op, elemTp, iter); + rewriter.replaceOp(op, dst); + return success(); +} + //===----------------------------------------------------------------------===// // Conversion rules. //===----------------------------------------------------------------------===// @@ -423,6 +550,7 @@ /// Sparse conversion rule for trivial tensor casts. class SparseCastConverter : public OpConversionPattern { +public: using OpConversionPattern::OpConversionPattern; LogicalResult matchAndRewrite(tensor::CastOp op, OpAdaptor adaptor, @@ -437,8 +565,49 @@ } }; +/// Sparse conversion rule for the expand reshape operator. +class SparseExpandConverter + : public OpConversionPattern { +public: + using OpConversionPattern::OpConversionPattern; + LogicalResult + matchAndRewrite(tensor::ExpandShapeOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + Type dstType = op.result().getType(); + Type srcType = op.src().getType(); + auto encDst = getSparseTensorEncoding(dstType); + auto encSrc = getSparseTensorEncoding(srcType); + if (encDst && encSrc) + return genSparse2SparseReshape( + op, rewriter, op.getReassociationIndices(), adaptor.getOperands()[0], + dstType.cast(), srcType.cast()); + return failure(); // handled elsewhere + } +}; + +/// Sparse conversion rule for the collapse reshape operator. +class SparseCollapseConverter + : public OpConversionPattern { +public: + using OpConversionPattern::OpConversionPattern; + LogicalResult + matchAndRewrite(tensor::CollapseShapeOp op, OpAdaptor adaptor, + ConversionPatternRewriter &rewriter) const override { + Type dstType = op.result().getType(); + Type srcType = op.src().getType(); + auto encDst = getSparseTensorEncoding(dstType); + auto encSrc = getSparseTensorEncoding(srcType); + if (encDst && encSrc) + return genSparse2SparseReshape( + op, rewriter, op.getReassociationIndices(), adaptor.getOperands()[0], + dstType.cast(), srcType.cast()); + return failure(); // handled elsewhere + } +}; + /// Sparse conversion rule for the new operator. class SparseTensorNewConverter : public OpConversionPattern { +public: using OpConversionPattern::OpConversionPattern; LogicalResult matchAndRewrite(NewOp op, OpAdaptor adaptor, @@ -463,6 +632,7 @@ /// Sparse conversion rule for the alloc operator. class SparseTensorAllocConverter : public OpConversionPattern { +public: using OpConversionPattern::OpConversionPattern; LogicalResult matchAndRewrite(bufferization::AllocTensorOp op, OpAdaptor adaptor, @@ -494,9 +664,6 @@ /// Sparse conversion rule for the convert operator. class SparseTensorConvertConverter : public OpConversionPattern { - /// Options to control sparse code generation. - SparseTensorConversionOptions options; - public: using OpConversionPattern::OpConversionPattern; SparseTensorConvertConverter(MLIRContext *context, @@ -697,6 +864,10 @@ rewriter.replaceOp(op, dst); return success(); } + +private: + /// Options to control sparse code generation. + SparseTensorConversionOptions options; }; /// Sparse conversion rule for the release operator. @@ -799,6 +970,7 @@ } }; +/// Sparse conversion rule for the expand operator. class SparseTensorExpandConverter : public OpConversionPattern { public: using OpConversionPattern::OpConversionPattern; @@ -841,6 +1013,7 @@ } }; +/// Sparse conversion rule for the compress operator. class SparseTensorCompressConverter : public OpConversionPattern { public: using OpConversionPattern::OpConversionPattern; @@ -873,6 +1046,7 @@ } }; +/// Sparse conversion rule for the output operator. class SparseTensorOutConverter : public OpConversionPattern { public: using OpConversionPattern::OpConversionPattern; @@ -926,6 +1100,7 @@ const SparseTensorConversionOptions &options) { patterns.add(); - target - .addLegalDialect(); + target.addLegalOp(); + target.addLegalDialect< + arith::ArithmeticDialect, bufferization::BufferizationDialect, + LLVM::LLVMDialect, memref::MemRefDialect, scf::SCFDialect>(); // Translate strategy flags to strategy options. SparseTensorConversionOptions options( sparseToSparseConversionStrategy(sparseToSparse)); diff --git a/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp b/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp --- a/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp +++ b/mlir/lib/Dialect/SparseTensor/Transforms/Sparsification.cpp @@ -1845,8 +1845,9 @@ // Since a pure dense expansion is very cheap (change of view), for // sparse2dense or dense2sparse, we can simply unfuse a sparse // conversion from the actual expansion operation itself. + // Other cases are handled elsewhere. if (encDst && encSrc) { - return failure(); // TODO: implement sparse2sparse + return failure(); } else if (encSrc) { RankedTensorType rtp = op.src().getType().cast(); auto denseTp = @@ -1882,8 +1883,9 @@ // Since a pure dense collapse is very cheap (change of view), for // sparse2dense or dense2sparse, we can simply unfuse a sparse // conversion from the actual collapse operation itself. + // Other cases are handled elsewhere. if (encDst && encSrc) { - return failure(); // TODO: implement sparse2sparse + return failure(); } else if (encSrc) { RankedTensorType rtp = op.src().getType().cast(); auto denseTp = diff --git a/mlir/test/Dialect/SparseTensor/rewriting.mlir b/mlir/test/Dialect/SparseTensor/rewriting.mlir old mode 100644 new mode 100755 --- a/mlir/test/Dialect/SparseTensor/rewriting.mlir +++ b/mlir/test/Dialect/SparseTensor/rewriting.mlir @@ -40,8 +40,14 @@ return %0 : tensor<3x4xf64, #SparseMatrix> } -// TODO: make this work +// +// Not rewritten, needs conversion. +// // CHECK-LABEL: func.func @expand_sparse2sparse( +// CHECK-SAME: %[[A:.*]]: tensor<12xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<3x4xf64, #sparse_tensor.encoding<{{{.*}}}>> { +// CHECK: %[[E:.*]] = tensor.expand_shape %[[A]] {{.*}} : tensor<12xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<3x4xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK: return %[[E]] : tensor<3x4xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK: } func.func @expand_sparse2sparse(%arg0: tensor<12xf64, #SparseVector>) -> tensor<3x4xf64, #SparseMatrix> { %0 = tensor.expand_shape %arg0 [[0, 1]] : tensor<12xf64, #SparseVector> into tensor<3x4xf64, #SparseMatrix> return %0 : tensor<3x4xf64, #SparseMatrix> @@ -79,8 +85,14 @@ return %0 : tensor<12xf64, #SparseVector> } -// TODO: make this work +// +// Not rewritten, needs conversion. +// // CHECK-LABEL: func.func @collapse_sparse2sparse( +// CHECK-SAME: %[[A:.*]]: tensor<3x4xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<12xf64, #sparse_tensor.encoding<{{{.*}}}>> { +// CHECK: %[[C:.*]] = tensor.collapse_shape %[[A]] {{.*}} : tensor<3x4xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<12xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK: return %[[C]] : tensor<12xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK: } func.func @collapse_sparse2sparse(%arg0: tensor<3x4xf64, #SparseMatrix>) -> tensor<12xf64, #SparseVector> { %0 = tensor.collapse_shape %arg0 [[0, 1]] : tensor<3x4xf64, #SparseMatrix> into tensor<12xf64, #SparseVector> return %0 : tensor<12xf64, #SparseVector> diff --git a/mlir/test/Dialect/SparseTensor/sparse_reshape.mlir b/mlir/test/Dialect/SparseTensor/sparse_reshape.mlir --- a/mlir/test/Dialect/SparseTensor/sparse_reshape.mlir +++ b/mlir/test/Dialect/SparseTensor/sparse_reshape.mlir @@ -1,24 +1,81 @@ -// RUN: mlir-opt %s | mlir-opt | FileCheck %s - -// TODO: check lowering to an actual implementation +// RUN: mlir-opt %s | mlir-opt | FileCheck %s --check-prefix=CHECK-ROUND +// RUN: mlir-opt %s --sparse-tensor-conversion --cse | FileCheck %s --check-prefix=CHECK-CONV #SparseVector = #sparse_tensor.encoding<{ dimLevelType = [ "compressed" ] }> #SparseMatrix = #sparse_tensor.encoding<{ dimLevelType = [ "compressed", "compressed" ] }> -// CHECK-LABEL: func.func @sparse_expand( -// CHECK-SAME: %[[A:.*]]: tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> -// CHECK: %[[E:.*]] = tensor.expand_shape %[[A]] {{\[\[}}0, 1]] : tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> -// CHECK: return %[[E]] : tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> +// +// roundtrip: +// +// CHECK-ROUND-LABEL: func.func @sparse_expand( +// CHECK-ROUND-SAME: %[[A:.*]]: tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK-ROUND: %[[E:.*]] = tensor.expand_shape %[[A]] {{\[\[}}0, 1]] : tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK-ROUND: return %[[E]] : tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> +// +// conversion: +// +// CHECK-CONV-LABEL: func.func @sparse_expand( +// CHECK-CONV-DAG: %[[C0:.*]] = arith.constant 0 : index +// CHECK-CONV-DAG: %[[C1:.*]] = arith.constant 1 : index +// CHECK-CONV-DAG: %[[C10:.*]] = arith.constant 10 : index +// CHECK-CONV-DAG: call @newSparseTensor +// CHECK-CONV-DAG: call @newSparseTensor +// CHECK-CONV: scf.while : () -> () { +// CHECK-CONV: call @getNextF64 +// CHECK-CONV: scf.condition(%13) +// CHECK-CONV: } do { +// CHECK-CONV: %[[X:.*]] = memref.load %{{.*}}[%[[C0]]] : memref +// CHECK-CONV: %[[D:.*]] = arith.divui %[[X]], %[[C10]] : index +// CHECK-CONV: memref.store %[[D]], %{{.*}}[%[[C0]]] : memref +// CHECK-CONV: %[[R:.*]] = arith.remui %[[X]], %[[C10]] : index +// CHECK-CONV: memref.store %[[R]], %{{.*}}[%[[C1]]] : memref +// CHECK-CONV: call @addEltF64 +// CHECK-CONV: scf.yield +// CHECK-CONV: } +// CHECK-CONV: %[[N:.*]] = call @newSparseTensor +// CHECK-CONV: call @delSparseTensorCOOF64 +// CHECK-CONV: call @delSparseTensorCOOF64 +// CHECK-CONV: return %[[N]] : !llvm.ptr +// func.func @sparse_expand(%arg0: tensor<100xf64, #SparseVector>) -> tensor<10x10xf64, #SparseMatrix> { %0 = tensor.expand_shape %arg0 [[0, 1]] : tensor<100xf64, #SparseVector> into tensor<10x10xf64, #SparseMatrix> return %0 : tensor<10x10xf64, #SparseMatrix> } -// CHECK-LABEL: func.func @sparse_collapse( -// CHECK-SAME: %[[A:.*]]: tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> -// CHECK: %[[C:.*]] = tensor.collapse_shape %[[A]] {{\[\[}}0, 1]] : tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> -// CHECK: return %[[C]] : tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> +// +// roundtrip: +// +// CHECK-ROUND-LABEL: func.func @sparse_collapse( +// CHECK-ROUND-SAME: %[[A:.*]]: tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>>) -> tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK-ROUND: %[[C:.*]] = tensor.collapse_shape %[[A]] {{\[\[}}0, 1]] : tensor<10x10xf64, #sparse_tensor.encoding<{{{.*}}}>> into tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> +// CHECK-ROUND: return %[[C]] : tensor<100xf64, #sparse_tensor.encoding<{{{.*}}}>> +// +// conversion: +// +// CHECK-CONV-LABEL: func.func @sparse_collapse( +// CHECK-CONV-DAG: %[[C0:.*]] = arith.constant 0 : index +// CHECK-CONV-DAG: %[[C1:.*]] = arith.constant 1 : index +// CHECK-CONV-DAG: %[[C10:.*]] = arith.constant 10 : index +// CHECK-CONV-DAG: call @newSparseTensor +// CHECK-CONV-DAG: call @newSparseTensor +// CHECK-CONV: scf.while : () -> () { +// CHECK-CONV: call @getNextF64 +// CHECK-CONV: scf.condition(%13) +// CHECK-CONV: } do { +// CHECK-CONV: %[[X:.*]] = memref.load %{{.*}}[%[[C0]]] : memref +// CHECK-CONV: %[[M:.*]] = arith.muli %[[X]], %[[C10]] : index +// CHECK-CONV: %[[Y:.*]] = memref.load %{{.*}}[%[[C1]]] : memref +// CHECK-CONV: %[[A:.*]] = arith.addi %[[M]], %[[Y]] : index +// CHECK-CONV: memref.store %[[A]], %{{.*}}[%[[C0]]] : memref +// CHECK-CONV: call @addEltF64 +// CHECK-CONV: scf.yield +// CHECK-CONV: } +// CHECK-CONV: %[[N:.*]] = call @newSparseTensor +// CHECK-CONV: call @delSparseTensorCOOF64 +// CHECK-CONV: call @delSparseTensorCOOF64 +// CHECK-CONV: return %[[N]] : !llvm.ptr +// func.func @sparse_collapse(%arg0: tensor<10x10xf64, #SparseMatrix>) -> tensor<100xf64, #SparseVector> { %0 = tensor.collapse_shape %arg0 [[0, 1]] : tensor<10x10xf64, #SparseMatrix> into tensor<100xf64, #SparseVector> diff --git a/mlir/test/Integration/Dialect/SparseTensor/CPU/sparse_reshape.mlir b/mlir/test/Integration/Dialect/SparseTensor/CPU/sparse_reshape.mlir old mode 100644 new mode 100755 --- a/mlir/test/Integration/Dialect/SparseTensor/CPU/sparse_reshape.mlir +++ b/mlir/test/Integration/Dialect/SparseTensor/CPU/sparse_reshape.mlir @@ -32,11 +32,10 @@ return %0 : tensor<3x4xf64, #SparseMatrix> } -// TODO: make this work -// func.func @expand_sparse2sparse(%arg0: tensor<12xf64, #SparseVector>) -> tensor<3x4xf64, #SparseMatrix> { -// %0 = tensor.expand_shape %arg0 [[0, 1]] : tensor<12xf64, #SparseVector> into tensor<3x4xf64, #SparseMatrix> -// return %0 : tensor<3x4xf64, #SparseMatrix> -// } + func.func @expand_sparse2sparse(%arg0: tensor<12xf64, #SparseVector>) -> tensor<3x4xf64, #SparseMatrix> { + %0 = tensor.expand_shape %arg0 [[0, 1]] : tensor<12xf64, #SparseVector> into tensor<3x4xf64, #SparseMatrix> + return %0 : tensor<3x4xf64, #SparseMatrix> + } func.func @collapse_dense(%arg0: tensor<3x4xf64>) -> tensor<12xf64> { %0 = tensor.collapse_shape %arg0 [[0, 1]] : tensor<3x4xf64> into tensor<12xf64> @@ -53,11 +52,10 @@ return %0 : tensor<12xf64, #SparseVector> } -// TODO: make this work -// func.func @collapse_sparse2sparse(%arg0: tensor<3x4xf64, #SparseMatrix>) -> tensor<12xf64, #SparseVector> { -// %0 = tensor.collapse_shape %arg0 [[0, 1]] : tensor<3x4xf64, #SparseMatrix> into tensor<12xf64, #SparseVector> -// return %0 : tensor<12xf64, #SparseVector> -// } + func.func @collapse_sparse2sparse(%arg0: tensor<3x4xf64, #SparseMatrix>) -> tensor<12xf64, #SparseVector> { + %0 = tensor.collapse_shape %arg0 [[0, 1]] : tensor<3x4xf64, #SparseMatrix> into tensor<12xf64, #SparseVector> + return %0 : tensor<12xf64, #SparseVector> + } // @@ -81,10 +79,12 @@ %expand0 = call @expand_dense(%v) : (tensor<12xf64>) -> tensor<3x4xf64> %expand1 = call @expand_from_sparse(%sv) : (tensor<12xf64, #SparseVector>) -> tensor<3x4xf64> %expand2 = call @expand_to_sparse(%v) : (tensor<12xf64>) -> tensor<3x4xf64, #SparseMatrix> + %expand3 = call @expand_sparse2sparse(%sv) : (tensor<12xf64, #SparseVector>) -> tensor<3x4xf64, #SparseMatrix> %collapse0 = call @collapse_dense(%m) : (tensor<3x4xf64>) -> tensor<12xf64> %collapse1 = call @collapse_from_sparse(%sm) : (tensor<3x4xf64, #SparseMatrix>) -> tensor<12xf64> %collapse2 = call @collapse_to_sparse(%m) : (tensor<3x4xf64>) -> tensor<12xf64, #SparseVector> + %collapse3 = call @collapse_sparse2sparse(%sm) : (tensor<3x4xf64, #SparseMatrix>) -> tensor<12xf64, #SparseVector> // // Verify result. @@ -92,9 +92,11 @@ // CHECK: ( ( 1, 2, 3, 4 ), ( 5, 6, 7, 8 ), ( 9, 10, 11, 12 ) ) // CHECK-NEXT: ( ( 1, 2, 3, 4 ), ( 5, 6, 7, 8 ), ( 9, 10, 11, 12 ) ) // CHECK-NEXT: ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, -1 ) + // CHECK-NEXT: ( 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, -1, -1, -1, -1 ) // CHECK-NEXT: ( 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4 ) // CHECK-NEXT: ( 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4 ) // CHECK-NEXT: ( 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, -1, -1, -1, -1 ) + // CHECK-NEXT: ( 1.1, 1.2, 1.3, 1.4, 2.1, 2.2, 2.3, 2.4, 3.1, 3.2, 3.3, 3.4, -1, -1, -1, -1 ) // %m0 = vector.transfer_read %expand0[%c0, %c0], %df: tensor<3x4xf64>, vector<3x4xf64> vector.print %m0 : vector<3x4xf64> @@ -103,6 +105,9 @@ %a2 = sparse_tensor.values %expand2 : tensor<3x4xf64, #SparseMatrix> to memref %m2 = vector.transfer_read %a2[%c0], %df: memref, vector<16xf64> vector.print %m2 : vector<16xf64> + %a3 = sparse_tensor.values %expand3 : tensor<3x4xf64, #SparseMatrix> to memref + %m3 = vector.transfer_read %a3[%c0], %df: memref, vector<16xf64> + vector.print %m3 : vector<16xf64> %v0 = vector.transfer_read %collapse0[%c0], %df: tensor<12xf64>, vector<12xf64> vector.print %v0 : vector<12xf64> @@ -111,12 +116,17 @@ %b2 = sparse_tensor.values %collapse2 : tensor<12xf64, #SparseVector> to memref %v2 = vector.transfer_read %b2[%c0], %df: memref, vector<16xf64> vector.print %v2 : vector<16xf64> + %b3 = sparse_tensor.values %collapse3 : tensor<12xf64, #SparseVector> to memref + %v3 = vector.transfer_read %b3[%c0], %df: memref, vector<16xf64> + vector.print %v3 : vector<16xf64> // Release sparse resources. sparse_tensor.release %sv : tensor<12xf64, #SparseVector> sparse_tensor.release %sm : tensor<3x4xf64, #SparseMatrix> sparse_tensor.release %expand2 : tensor<3x4xf64, #SparseMatrix> + sparse_tensor.release %expand3 : tensor<3x4xf64, #SparseMatrix> sparse_tensor.release %collapse2 : tensor<12xf64, #SparseVector> + sparse_tensor.release %collapse3 : tensor<12xf64, #SparseVector> // Release dense resources. %meme1 = bufferization.to_memref %expand1 : memref<3x4xf64>