MLIR 23.0.0git
CodegenUtils.cpp
Go to the documentation of this file.
1//===- CodegenUtils.cpp - Utilities for generating MLIR -------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9#include "CodegenUtils.h"
10
16#include "mlir/IR/Types.h"
17#include "mlir/IR/Value.h"
18#include <optional>
19
20using namespace mlir;
21using namespace mlir::sparse_tensor;
22
23//===----------------------------------------------------------------------===//
24// ExecutionEngine/SparseTensorUtils helper functions.
25//===----------------------------------------------------------------------===//
26
28 switch (width) {
29 case 64:
30 return OverheadType::kU64;
31 case 32:
32 return OverheadType::kU32;
33 case 16:
34 return OverheadType::kU16;
35 case 8:
36 return OverheadType::kU8;
37 case 0:
39 }
40 llvm_unreachable("Unsupported overhead bitwidth");
41}
42
44 if (tp.isIndex())
46 if (auto intTp = dyn_cast<IntegerType>(tp))
47 return overheadTypeEncoding(intTp.getWidth());
48 llvm_unreachable("Unknown overhead type");
49}
50
52 switch (ot) {
54 return builder.getIndexType();
56 return builder.getIntegerType(64);
58 return builder.getIntegerType(32);
60 return builder.getIntegerType(16);
62 return builder.getIntegerType(8);
63 }
64 llvm_unreachable("Unknown OverheadType");
65}
66
68mlir::sparse_tensor::posTypeEncoding(SparseTensorEncodingAttr enc) {
69 return overheadTypeEncoding(enc.getPosWidth());
70}
71
73mlir::sparse_tensor::crdTypeEncoding(SparseTensorEncodingAttr enc) {
74 return overheadTypeEncoding(enc.getCrdWidth());
75}
76
77// TODO: we ought to add some `static_assert` tests to ensure that the
78// `STEA::get{Pos,Crd}Type` methods agree with `getOverheadType(builder,
79// {pos,crd}OverheadTypeEncoding(enc))`
80
81// TODO: Adjust the naming convention for the constructors of
82// `OverheadType` so we can use the `MLIR_SPARSETENSOR_FOREVERY_O` x-macro
83// here instead of `MLIR_SPARSETENSOR_FOREVERY_FIXED_O`; to further reduce
84// the possibility of typo bugs or things getting out of sync.
86 switch (ot) {
88 return "0";
89#define CASE(ONAME, O) \
90 case OverheadType::kU##ONAME: \
91 return #ONAME;
93#undef CASE
94 }
95 llvm_unreachable("Unknown OverheadType");
96}
97
101
103 if (elemTp.isF64() || elemTp.isF32() || elemTp.isF16() || elemTp.isBF16() ||
104 elemTp.isInteger(64) || elemTp.isInteger(32) || elemTp.isInteger(16) ||
105 elemTp.isInteger(8))
106 return true;
107 if (auto complexTp = dyn_cast<ComplexType>(elemTp)) {
108 Type elt = complexTp.getElementType();
109 return elt.isF64() || elt.isF32();
110 }
111 return false;
112}
113
115 if (elemTp.isF64())
116 return PrimaryType::kF64;
117 if (elemTp.isF32())
118 return PrimaryType::kF32;
119 if (elemTp.isF16())
120 return PrimaryType::kF16;
121 if (elemTp.isBF16())
122 return PrimaryType::kBF16;
123 if (elemTp.isInteger(64))
124 return PrimaryType::kI64;
125 if (elemTp.isInteger(32))
126 return PrimaryType::kI32;
127 if (elemTp.isInteger(16))
128 return PrimaryType::kI16;
129 if (elemTp.isInteger(8))
130 return PrimaryType::kI8;
131 if (auto complexTp = dyn_cast<ComplexType>(elemTp)) {
132 auto complexEltTp = complexTp.getElementType();
133 if (complexEltTp.isF64())
134 return PrimaryType::kC64;
135 if (complexEltTp.isF32())
136 return PrimaryType::kC32;
137 }
138 llvm_unreachable("Unknown primary type");
139}
140
142 switch (pt) {
143#define CASE(VNAME, V) \
144 case PrimaryType::k##VNAME: \
145 return #VNAME;
147#undef CASE
148 }
149 llvm_unreachable("Unknown PrimaryType");
150}
151
155
156//===----------------------------------------------------------------------===//
157// Misc code generators.
158//===----------------------------------------------------------------------===//
159
161 Type dstTp) {
162 const Type srcTp = value.getType();
163 if (srcTp == dstTp)
164 return value;
165
166 // int <=> index
167 if (isa<IndexType>(srcTp) || isa<IndexType>(dstTp))
168 return arith::IndexCastOp::create(builder, loc, dstTp, value);
169
170 const auto srcIntTp = dyn_cast_or_null<IntegerType>(srcTp);
171 const bool isUnsignedCast = srcIntTp ? srcIntTp.isUnsigned() : false;
172 return mlir::convertScalarToDtype(builder, loc, value, dstTp, isUnsignedCast);
173}
174
176 Value elem, Type dstTp) {
177 if (auto rtp = dyn_cast<RankedTensorType>(dstTp)) {
178 // Scalars can only be converted to 0-ranked tensors.
179 assert(rtp.getRank() == 0);
180 elem = sparse_tensor::genCast(builder, loc, elem, rtp.getElementType());
181 return tensor::FromElementsOp::create(builder, loc, rtp, elem);
182 }
183 return sparse_tensor::genCast(builder, loc, elem, dstTp);
184}
185
187 ValueRange s) {
188 Value load = memref::LoadOp::create(builder, loc, mem, s);
189 if (!isa<IndexType>(load.getType())) {
190 if (load.getType().getIntOrFloatBitWidth() < 64)
191 load = arith::ExtUIOp::create(builder, loc, builder.getI64Type(), load);
192 load =
193 arith::IndexCastOp::create(builder, loc, builder.getIndexType(), load);
194 }
195 return load;
196}
197
198mlir::TypedAttr mlir::sparse_tensor::getOneAttr(Builder &builder, Type tp) {
199 if (isa<FloatType>(tp))
200 return builder.getFloatAttr(tp, 1.0);
201 if (isa<IndexType>(tp))
202 return builder.getIndexAttr(1);
203 if (auto intTp = dyn_cast<IntegerType>(tp))
204 return builder.getIntegerAttr(tp, APInt(intTp.getWidth(), 1));
205 if (isa<RankedTensorType, VectorType>(tp)) {
206 auto shapedTp = cast<ShapedType>(tp);
207 if (auto one = getOneAttr(builder, shapedTp.getElementType()))
208 return DenseElementsAttr::get(shapedTp, one);
209 }
210 llvm_unreachable("Unsupported attribute type");
211}
212
214 Value v) {
215 Type tp = v.getType();
216 Value zero = constantZero(builder, loc, tp);
217 if (isa<FloatType>(tp))
218 return arith::CmpFOp::create(builder, loc, arith::CmpFPredicate::UNE, v,
219 zero);
220 if (tp.isIntOrIndex())
221 return arith::CmpIOp::create(builder, loc, arith::CmpIPredicate::ne, v,
222 zero);
223 if (isa<ComplexType>(tp))
224 return complex::NotEqualOp::create(builder, loc, v, zero);
225 llvm_unreachable("Non-numeric type");
226}
227
229 OpBuilder &builder, Location loc, SmallVectorImpl<Value> &dstShape,
230 ArrayRef<Value> srcShape, ArrayRef<Size> staticDstShape,
231 ArrayRef<ReassociationIndices> reassociation) {
232 // Collapse shape.
233 if (reassociation.size() < srcShape.size()) {
234 unsigned start = 0;
235 for (const auto &map : llvm::enumerate(reassociation)) {
236 auto dstDim = constantIndex(builder, loc, 1);
237 for (unsigned i = start; i < start + map.value().size(); i++) {
238 dstDim = arith::MulIOp::create(builder, loc, dstDim, srcShape[i]);
239 }
240 dstShape.push_back(dstDim);
241 start = start + map.value().size();
242 }
243 assert(start == srcShape.size());
244 return;
245 }
246
247 // Expand shape.
248 assert(reassociation.size() == srcShape.size());
249 unsigned start = 0;
250 // Expand the i-th dimension in srcShape.
251 for (unsigned i = 0, size = srcShape.size(); i < size; i++) {
252 const auto &map = reassociation[i];
253 auto srcDim = srcShape[i];
254 // Iterate through dimensions expanded from the i-th dimension.
255 for (unsigned j = start; j < start + map.size(); j++) {
256 // There can be only one dynamic sized dimension among dimensions
257 // expanded from the i-th dimension in srcShape.
258 // For example, if srcDim = 8, then the expanded shape could be <2x?x2>,
259 // but not <2x?x?>.
260 if (staticDstShape[j] == ShapedType::kDynamic) {
261 // The expanded dimension has dynamic size. We compute the dimension
262 // by dividing srcDim by the product of the static dimensions.
263 Size product = 1;
264 for (unsigned k = start; k < start + map.size(); k++) {
265 if (staticDstShape[k] != ShapedType::kDynamic) {
266 product *= staticDstShape[k];
267 }
268 }
269 // Compute the dynamic dimension size.
270 Value productVal = constantIndex(builder, loc, product);
271 Value dynamicSize =
272 arith::DivUIOp::create(builder, loc, srcDim, productVal);
273 dstShape.push_back(dynamicSize);
274 } else {
275 // The expanded dimension is statically known.
276 dstShape.push_back(constantIndex(builder, loc, staticDstShape[j]));
277 }
278 }
279 start = start + map.size();
280 }
281 assert(start == staticDstShape.size());
282}
283
285 OpBuilder &builder, Location loc,
286 ArrayRef<ReassociationIndices> reassociation, // NOLINT
287 ValueRange srcSizes, ValueRange srcCvs, // NOLINT
288 ValueRange dstSizes, SmallVectorImpl<Value> &dstCvs) {
289 const unsigned srcRank = srcSizes.size();
290 const unsigned dstRank = dstSizes.size();
291 assert(srcRank == srcCvs.size() && "Source rank mismatch");
292 const bool isCollapse = srcRank > dstRank;
293 const ValueRange sizes = isCollapse ? srcSizes : dstSizes;
294 // Iterate over reassociation map.
295 unsigned i = 0;
296 unsigned start = 0;
297 for (const auto &map : llvm::enumerate(reassociation)) {
298 // Prepare strides information in dimension slice.
299 Value linear = constantIndex(builder, loc, 1);
300 for (unsigned j = start, end = start + map.value().size(); j < end; j++) {
301 linear = arith::MulIOp::create(builder, loc, linear, sizes[j]);
302 }
303 // Start expansion.
304 Value val;
305 if (!isCollapse)
306 val = srcCvs[i];
307 // Iterate over dimension slice.
308 for (unsigned j = start, end = start + map.value().size(); j < end; j++) {
309 linear = arith::DivUIOp::create(builder, loc, linear, sizes[j]);
310 if (isCollapse) {
311 const Value mul =
312 arith::MulIOp::create(builder, loc, srcCvs[j], linear);
313 val = val ? arith::AddIOp::create(builder, loc, val, mul) : mul;
314 } else {
315 const Value old = val;
316 val = arith::DivUIOp::create(builder, loc, val, linear);
317 assert(dstCvs.size() == j);
318 dstCvs.push_back(val);
319 val = arith::RemUIOp::create(builder, loc, old, linear);
320 }
321 }
322 // Finalize collapse.
323 if (isCollapse) {
324 assert(dstCvs.size() == i);
325 dstCvs.push_back(val);
326 }
327 start += map.value().size();
328 i++;
329 }
330 assert(dstCvs.size() == dstRank);
331}
332
333FlatSymbolRefAttr mlir::sparse_tensor::getFunc(ModuleOp module, StringRef name,
334 TypeRange resultType,
335 ValueRange operands,
336 EmitCInterface emitCInterface) {
337 MLIRContext *context = module.getContext();
338 auto result = SymbolRefAttr::get(context, name);
339 auto func = module.lookupSymbol<func::FuncOp>(result.getAttr());
340 if (!func) {
341 OpBuilder moduleBuilder(module.getBodyRegion());
342 func = func::FuncOp::create(
343 moduleBuilder, module.getLoc(), name,
344 FunctionType::get(context, operands.getTypes(), resultType));
345 func.setPrivate();
346 if (static_cast<bool>(emitCInterface))
347 func->setAttr(LLVM::LLVMDialect::getEmitCWrapperAttrName(),
348 UnitAttr::get(context));
349 }
350 return result;
351}
352
354 OpBuilder &builder, Location loc, StringRef name, TypeRange resultType,
355 ValueRange operands, EmitCInterface emitCInterface) {
356 auto module = builder.getBlock()->getParentOp()->getParentOfType<ModuleOp>();
358 getFunc(module, name, resultType, operands, emitCInterface);
359 return func::CallOp::create(builder, loc, resultType, fn, operands);
360}
361
363 return LLVM::LLVMPointerType::get(ctx);
364}
365
369
371 unsigned sz, Type tp, bool staticShape) {
372 if (staticShape) {
373 auto memTp = MemRefType::get({sz}, tp);
374 return memref::AllocaOp::create(builder, loc, memTp);
375 }
376 return genAlloca(builder, loc, constantIndex(builder, loc, sz), tp);
377}
378
380 Type tp) {
381 auto memTp = MemRefType::get({ShapedType::kDynamic}, tp);
382 return memref::AllocaOp::create(builder, loc, memTp, ValueRange{sz});
383}
384
386 Type tp) {
387 return memref::AllocaOp::create(builder, loc, MemRefType::get({}, tp));
388}
389
391 ValueRange values) {
392 const unsigned sz = values.size();
393 assert(sz >= 1);
394 Value buffer = genAlloca(builder, loc, sz, values[0].getType());
395 for (unsigned i = 0; i < sz; i++) {
396 Value idx = constantIndex(builder, loc, i);
397 memref::StoreOp::create(builder, loc, values[i], buffer, idx);
398 }
399 return buffer;
400}
401
403 RankedTensorType tensorTp,
404 ValueRange sizes) {
405 Type elemTp = tensorTp.getElementType();
406 auto shape = tensorTp.getShape();
407 auto memTp = MemRefType::get(shape, elemTp);
408 SmallVector<Value> dynamicSizes;
409 for (unsigned i = 0, rank = tensorTp.getRank(); i < rank; i++) {
410 if (shape[i] == ShapedType::kDynamic)
411 dynamicSizes.push_back(sizes[i]);
412 }
413 Value mem = memref::AllocOp::create(builder, loc, memTp, dynamicSizes);
414 Value zero = constantZero(builder, loc, elemTp);
415 linalg::FillOp::create(builder, loc, ValueRange{zero}, ValueRange{mem});
416 return mem;
417}
418
420 Value buffer) {
421 memref::DeallocOp::create(builder, loc, buffer);
422}
423
426 Location loc, Value src) {
427 const Dimension dimRank = getSparseTensorType(src).getDimRank();
428 for (Dimension d = 0; d < dimRank; d++)
429 sizes.push_back(linalg::createOrFoldDimOp(builder, loc, src, d));
430}
431
433 for (; isa<scf::ForOp, scf::WhileOp, scf::ParallelOp, scf::IfOp>(
434 op->getParentOp());
435 op = op->getParentOp())
436 ;
437 return op;
438}
439
441 OpBuilder &builder, Location loc, SparseElementsAttr attr, AffineMap order,
442 function_ref<void(ArrayRef<Value>, Value)> callback) {
443 if (!order)
444 order = builder.getMultiDimIdentityMap(attr.getType().getRank());
445
446 auto stt = SparseTensorType(getRankedTensorType(attr));
447 const Dimension dimRank = stt.getDimRank();
448 const auto coordinates = attr.getIndices().getValues<IntegerAttr>();
449 const auto values = attr.getValues().getValues<Attribute>();
450
451 // This is like the `Element<V>` class in the runtime library, but for
452 // MLIR attributes. In the future we may want to move this out into
453 // a proper class definition to help improve code legibility (e.g.,
454 // `first` -> `coords`, `second` -> `value`) as well as being able
455 // to factor out analogues of `ElementLT<V>` for the sort below, etc.
456 using ElementAttr = std::pair<SmallVector<IntegerAttr>, Attribute>;
457
458 // Construct the COO from the SparseElementsAttr.
460 for (size_t i = 0, nse = values.size(); i < nse; i++) {
461 elems.emplace_back();
462 elems.back().second = values[i];
463 auto &coords = elems.back().first;
464 coords.reserve(dimRank);
465 for (Dimension d = 0; d < dimRank; d++)
466 coords.push_back(coordinates[i * dimRank + d]);
467 }
468
469 // Sorts the sparse element attribute based on coordinates.
470 llvm::sort(elems, [order](const ElementAttr &lhs, const ElementAttr &rhs) {
471 if (std::addressof(lhs) == std::addressof(rhs))
472 return false;
473
474 auto lhsCoords = llvm::map_to_vector(
475 lhs.first, [](IntegerAttr i) { return i.getInt(); });
476 auto rhsCoords = llvm::map_to_vector(
477 rhs.first, [](IntegerAttr i) { return i.getInt(); });
478
479 SmallVector<int64_t, 4> lhsLvlCrds = order.compose(lhsCoords);
480 SmallVector<int64_t, 4> rhsLvlCrds = order.compose(rhsCoords);
481 // Sort the element based on the lvl coordinates.
482 for (Level l = 0; l < order.getNumResults(); l++) {
483 if (lhsLvlCrds[l] == rhsLvlCrds[l])
484 continue;
485 return lhsLvlCrds[l] < rhsLvlCrds[l];
486 }
487 llvm_unreachable("no equal coordinate in sparse element attr");
488 });
489
491 cvs.reserve(dimRank);
492 for (size_t i = 0, nse = values.size(); i < nse; i++) {
493 // Remap coordinates.
494 cvs.clear();
495 for (Dimension d = 0; d < dimRank; d++) {
496 auto crd = elems[i].first[d].getInt();
497 cvs.push_back(arith::ConstantIndexOp::create(builder, loc, crd));
498 }
499 // Remap value.
500 Value val;
501 if (isa<ComplexType>(attr.getElementType())) {
502 auto valAttr = cast<ArrayAttr>(elems[i].second);
503 val = complex::ConstantOp::create(builder, loc, attr.getElementType(),
504 valAttr);
505 } else {
506 auto valAttr = cast<TypedAttr>(elems[i].second);
507 val = arith::ConstantOp::create(builder, loc, valAttr);
508 }
509 assert(val);
510 callback(cvs, val);
511 }
512}
513
515 size_t size, Value mem,
516 size_t offsetIdx, Value offsetVal) {
517#ifndef NDEBUG
518 const auto memTp = cast<MemRefType>(mem.getType());
519 assert(memTp.getRank() == 1);
520 const Size memSh = memTp.getDimSize(0);
521 assert(ShapedType::isDynamic(memSh) || memSh >= static_cast<Size>(size));
522 assert(offsetIdx == 0 || offsetIdx < size);
523#endif // NDEBUG
525 vs.reserve(size);
526 for (unsigned i = 0; i < size; i++) {
527 Value v = memref::LoadOp::create(builder, loc, mem,
528 constantIndex(builder, loc, i));
529 if (i == offsetIdx && offsetVal)
530 v = arith::AddIOp::create(builder, loc, v, offsetVal);
531 vs.push_back(v);
532 }
533 return vs;
534}
535
537 ValueRange vs, size_t offsetIdx, Value offsetVal) {
538#ifndef NDEBUG
539 const size_t vsize = vs.size();
540 const auto memTp = cast<MemRefType>(mem.getType());
541 assert(memTp.getRank() == 1);
542 const Size memSh = memTp.getDimSize(0);
543 assert(ShapedType::isDynamic(memSh) || memSh >= static_cast<Size>(vsize));
544 assert(offsetIdx == 0 || offsetIdx < vsize);
545#endif // NDEBUG
546 for (const auto &v : llvm::enumerate(vs)) {
547 const Value w =
548 (offsetIdx == v.index() && offsetVal)
549 ? arith::AddIOp::create(builder, loc, v.value(), offsetVal)
550 : v.value();
551 memref::StoreOp::create(builder, loc, w, mem,
552 constantIndex(builder, loc, v.index()));
553 }
554}
555
558 auto tTp = llvm::cast<TensorType>(tensor.getType());
559 auto mTp = MemRefType::get(tTp.getShape(), tTp.getElementType());
560 return cast<TypedValue<BaseMemRefType>>(
561 bufferization::ToBufferOp::create(builder, loc, mTp, tensor).getResult());
562}
563
565 Value tensor, Dimension dim) {
566 auto enc = getSparseTensorEncoding(tensor.getType());
567 assert(enc && enc.isSlice());
568 std::optional<unsigned> offset = enc.getStaticDimSliceOffset(dim);
569 if (offset.has_value())
570 return constantIndex(builder, loc, *offset);
571 return ToSliceOffsetOp::create(builder, loc, tensor, APInt(64, dim));
572}
573
575 Value tensor, Dimension dim) {
576 auto enc = getSparseTensorEncoding(tensor.getType());
577 assert(enc && enc.isSlice());
578 std::optional<unsigned> stride = enc.getStaticDimSliceStride(dim);
579 if (stride.has_value())
580 return constantIndex(builder, loc, *stride);
581 return ToSliceStrideOp::create(builder, loc, tensor, APInt(64, dim));
582}
583
586 /*out*/ SmallVectorImpl<Value> &dimSizesValues,
587 /*out*/ Value &dimSizesBuffer) {
588 // Construct the dimension **shapes** buffer. The buffer contains the static
589 // size per dimension, or otherwise a zero for a dynamic size.
590 Dimension dimRank = stt.getDimRank();
591 dimSizesValues.clear();
592 dimSizesValues.reserve(dimRank);
593 for (const Size sz : stt.getDimShape()) {
594 const auto s = ShapedType::isDynamic(sz) ? 0 : sz;
595 dimSizesValues.push_back(constantIndex(builder, loc, s));
596 }
597 Value dimShapesBuffer = allocaBuffer(builder, loc, dimSizesValues);
598 // Create the `CheckedSparseTensorReader`. This reader performs a
599 // consistency check on the static sizes, but accepts any size
600 // of each dimension with a dynamic size.
601 Type opaqueTp = getOpaquePointerType(builder);
602 Type eltTp = stt.getElementType();
603 Value valTp = constantPrimaryTypeEncoding(builder, loc, eltTp);
604 Value reader =
605 createFuncCall(builder, loc, "createCheckedSparseTensorReader", opaqueTp,
606 {tensor, dimShapesBuffer, valTp}, EmitCInterface::On)
607 .getResult(0);
608 // For static shapes, the shape buffer can be used right away. For dynamic
609 // shapes, use the information from the reader to construct a buffer that
610 // supplies the actual size for each dynamic dimension.
611 dimSizesBuffer = dimShapesBuffer;
612 if (stt.hasDynamicDimShape()) {
613 Type indexTp = builder.getIndexType();
614 auto memTp = MemRefType::get({ShapedType::kDynamic}, indexTp);
615 dimSizesBuffer =
616 createFuncCall(builder, loc, "getSparseTensorReaderDimSizes", memTp,
617 reader, EmitCInterface::On)
618 .getResult(0);
619 // Also convert the dim shapes values into dim sizes values, just in case
620 // subsequent clients need the values (DCE will remove unused).
621 for (Dimension d = 0; d < dimRank; d++) {
622 if (stt.isDynamicDim(d))
623 dimSizesValues[d] = memref::LoadOp::create(
624 builder, loc, dimSizesBuffer, constantIndex(builder, loc, d));
625 }
626 }
627 return reader;
628}
629
631 OpBuilder &builder, Location loc, SparseTensorType stt,
632 ArrayRef<Value> dimSizesValues, Value dimSizesBuffer,
633 /*out*/ SmallVectorImpl<Value> &lvlSizesValues,
634 /*out*/ Value &dim2lvlBuffer,
635 /*out*/ Value &lvl2dimBuffer) {
636 const Dimension dimRank = stt.getDimRank();
637 const Level lvlRank = stt.getLvlRank();
638 lvlSizesValues.clear();
639 lvlSizesValues.reserve(lvlRank);
640 // For an identity mapping, the dim2lvl and lvl2dim mappings are
641 // identical as are dimSizes and lvlSizes, so buffers are reused
642 // as much as possible.
643 if (stt.isIdentity()) {
644 assert(dimRank == lvlRank);
645 SmallVector<Value> iotaValues;
646 iotaValues.reserve(lvlRank);
647 for (Level l = 0; l < lvlRank; l++) {
648 iotaValues.push_back(constantIndex(builder, loc, l));
649 lvlSizesValues.push_back(dimSizesValues[l]);
650 }
651 dim2lvlBuffer = lvl2dimBuffer = allocaBuffer(builder, loc, iotaValues);
652 return dimSizesBuffer; // now lvlSizesBuffer
653 }
654 // Otherwise, some code needs to be generated to set up the buffers.
655 // This code deals with permutations as well as non-permutations that
656 // arise from rank changing blocking.
657 const auto dimToLvl = stt.getDimToLvl();
658 const auto lvlToDim = stt.getLvlToDim();
659 SmallVector<Value> dim2lvlValues(lvlRank); // for each lvl, expr in dim vars
660 SmallVector<Value> lvl2dimValues(dimRank); // for each dim, expr in lvl vars
661 // Generate dim2lvl.
662 assert(lvlRank == dimToLvl.getNumResults());
663 for (Level l = 0; l < lvlRank; l++) {
664 AffineExpr exp = dimToLvl.getResult(l);
665 // We expect:
666 // (1) l = d
667 // (2) l = d / c
668 // (3) l = d % c
669 Dimension d = 0;
670 uint64_t cf = 0, cm = 0;
671 switch (exp.getKind()) {
673 d = cast<AffineDimExpr>(exp).getPosition();
674 break;
675 }
677 auto floor = cast<AffineBinaryOpExpr>(exp);
678 d = cast<AffineDimExpr>(floor.getLHS()).getPosition();
679 cf = cast<AffineConstantExpr>(floor.getRHS()).getValue();
680 break;
681 }
682 case AffineExprKind::Mod: {
683 auto mod = cast<AffineBinaryOpExpr>(exp);
684 d = cast<AffineDimExpr>(mod.getLHS()).getPosition();
685 cm = cast<AffineConstantExpr>(mod.getRHS()).getValue();
686 break;
687 }
688 default:
689 llvm::report_fatal_error("unsupported dim2lvl in sparse tensor type");
690 }
691 dim2lvlValues[l] = constantIndex(builder, loc, encodeDim(d, cf, cm));
692 // Compute the level sizes.
693 // (1) l = d : size(d)
694 // (2) l = d / c : size(d) / c
695 // (3) l = d % c : c
696 Value lvlSz;
697 if (cm == 0) {
698 lvlSz = dimSizesValues[d];
699 if (cf != 0)
700 lvlSz = arith::DivUIOp::create(builder, loc, lvlSz,
701 constantIndex(builder, loc, cf));
702 } else {
703 lvlSz = constantIndex(builder, loc, cm);
704 }
705 lvlSizesValues.push_back(lvlSz);
706 }
707 // Generate lvl2dim.
708 assert(dimRank == lvlToDim.getNumResults());
709 for (Dimension d = 0; d < dimRank; d++) {
710 AffineExpr exp = lvlToDim.getResult(d);
711 // We expect:
712 // (1) d = l
713 // (2) d = l' * c + l
714 Level l = 0, ll = 0;
715 uint64_t c = 0;
716 switch (exp.getKind()) {
718 l = cast<AffineDimExpr>(exp).getPosition();
719 break;
720 }
721 case AffineExprKind::Add: {
722 // Always mul on lhs, symbol/constant on rhs.
723 auto add = cast<AffineBinaryOpExpr>(exp);
724 assert(add.getLHS().getKind() == AffineExprKind::Mul);
725 auto mul = cast<AffineBinaryOpExpr>(add.getLHS());
726 ll = cast<AffineDimExpr>(mul.getLHS()).getPosition();
727 c = cast<AffineConstantExpr>(mul.getRHS()).getValue();
728 l = cast<AffineDimExpr>(add.getRHS()).getPosition();
729 break;
730 }
731 default:
732 llvm::report_fatal_error("unsupported lvl2dim in sparse tensor type");
733 }
734 lvl2dimValues[d] = constantIndex(builder, loc, encodeLvl(l, c, ll));
735 }
736 // Return buffers.
737 dim2lvlBuffer = allocaBuffer(builder, loc, dim2lvlValues);
738 lvl2dimBuffer = allocaBuffer(builder, loc, lvl2dimValues);
739 return allocaBuffer(builder, loc, lvlSizesValues); // lvlSizesBuffer
740}
#define CASE(ONAME, O)
#define MLIR_SPARSETENSOR_FOREVERY_FIXED_O(DO)
Definition Enums.h:63
#define MLIR_SPARSETENSOR_FOREVERY_V(DO)
Definition Enums.h:96
static int64_t product(ArrayRef< int64_t > vals)
lhs
auto load
#define mul(a, b)
#define add(a, b)
Base type for affine expression.
Definition AffineExpr.h:68
AffineExprKind getKind() const
Return the classification for this type.
A multi-dimensional affine map Affine map's are immutable like Type's, and they are uniqued.
Definition AffineMap.h:46
unsigned getNumResults() const
AffineMap compose(AffineMap map) const
Returns the AffineMap resulting from composing this with map.
Attributes are known-constant values of operations.
Definition Attributes.h:25
This class is a general helper class for creating context-global objects like types,...
Definition Builders.h:51
IntegerAttr getIndexAttr(int64_t value)
Definition Builders.cpp:112
IntegerAttr getIntegerAttr(Type type, int64_t value)
Definition Builders.cpp:232
AffineMap getMultiDimIdentityMap(unsigned rank)
Definition Builders.cpp:391
FloatAttr getFloatAttr(Type type, double value)
Definition Builders.cpp:258
IntegerType getI64Type()
Definition Builders.cpp:69
IntegerType getIntegerType(unsigned width)
Definition Builders.cpp:71
MLIRContext * getContext() const
Definition Builders.h:56
IndexType getIndexType()
Definition Builders.cpp:55
static DenseElementsAttr get(ShapedType type, ArrayRef< Attribute > values)
Constructs a dense elements attribute from an array of element values.
A symbol reference with a reference path containing a single element.
This class defines the main interface for locations in MLIR and acts as a non-nullable wrapper around...
Definition Location.h:76
MLIRContext is the top-level object for a collection of MLIR operations.
Definition MLIRContext.h:63
This class helps build Operations.
Definition Builders.h:209
Operation is the basic unit of execution within MLIR.
Definition Operation.h:88
Operation * getParentOp()
Returns the closest surrounding operation that contains this operation or nullptr if this is a top-le...
Definition Operation.h:255
This class provides an abstraction over the various different ranges of value types.
Definition TypeRange.h:37
Instances of the Type class are uniqued, have an immutable identifier and an optional mutable compone...
Definition Types.h:74
bool isF64() const
Definition Types.cpp:41
bool isIndex() const
Definition Types.cpp:56
bool isF32() const
Definition Types.cpp:40
bool isIntOrIndex() const
Return true if this is an integer (of any signedness) or an index type.
Definition Types.cpp:114
bool isInteger() const
Return true if this is an integer type (with the specified width).
Definition Types.cpp:58
bool isF16() const
Definition Types.cpp:38
bool isBF16() const
Definition Types.cpp:37
This class provides an abstraction over the different types of ranges over Values.
Definition ValueRange.h:387
type_range getTypes() const
This class represents an instance of an SSA value in the MLIR system, representing a computable value...
Definition Value.h:96
Type getType() const
Return the type of this value.
Definition Value.h:105
static ConstantIndexOp create(OpBuilder &builder, Location location, int64_t value)
Definition ArithOps.cpp:363
A wrapper around RankedTensorType, which has three goals:
Dimension getDimRank() const
Returns the dimension-rank.
AffineMap getLvlToDim() const
Returns the lvlToDiml mapping (or the null-map for the identity).
bool isIdentity() const
Returns true if the dimToLvl mapping is the identity.
bool hasDynamicDimShape() const
Returns true if any dimension has dynamic size.
ArrayRef< Size > getDimShape() const
Returns the dimension-shape.
Level getLvlRank() const
Returns the level-rank.
bool isDynamicDim(Dimension d) const
Returns true if the given dimension has dynamic size.
AffineMap getDimToLvl() const
Returns the dimToLvl mapping (or the null-map for the identity).
Value createOrFoldDimOp(OpBuilder &b, Location loc, Value val, int64_t dim)
Create one memref::DimOp or tensor::DimOp depending on the type of val.
Definition LinalgOps.cpp:97
TypedAttr getOneAttr(Builder &builder, Type tp)
Generates a 1-valued attribute of the given type.
FlatSymbolRefAttr getFunc(ModuleOp module, StringRef name, TypeRange resultType, ValueRange operands, EmitCInterface emitCInterface)
Returns a function reference (first hit also inserts into module).
Value genAllocaScalar(OpBuilder &builder, Location loc, Type tp)
Generates an uninitialized temporary buffer with room for one value of the given type,...
Value constantIndex(OpBuilder &builder, Location loc, int64_t i)
Generates a constant of index type.
void foreachInSparseConstant(OpBuilder &builder, Location loc, SparseElementsAttr attr, AffineMap order, function_ref< void(ArrayRef< Value >, Value)> callback)
Iterate over a sparse constant, generates constantOp for value and coordinates.
Value constantZero(OpBuilder &builder, Location loc, Type tp)
Generates a 0-valued constant of the given type.
uint64_t Dimension
The type of dimension identifiers and dimension-ranks.
Value allocaBuffer(OpBuilder &builder, Location loc, ValueRange values)
Generates a temporary buffer, initializes it with the given contents, and returns it as type memref<?
OverheadType posTypeEncoding(SparseTensorEncodingAttr enc)
Returns the OverheadType for position overhead storage.
OverheadType
Encoding of overhead types (both position overhead and coordinate overhead), for "overloading" @newSp...
Definition Enums.h:51
OverheadType crdTypeEncoding(SparseTensorEncodingAttr enc)
Returns the OverheadType for coordinate overhead storage.
TypedValue< BaseMemRefType > genToMemref(OpBuilder &builder, Location loc, Value tensor)
OverheadType overheadTypeEncoding(unsigned width)
Converts an overhead storage bitwidth to its internal type-encoding.
Value genIndexLoad(OpBuilder &builder, Location loc, Value mem, ValueRange s)
Generates a pointer/index load from the sparse storage scheme.
StringRef overheadTypeFunctionSuffix(OverheadType ot)
Convert OverheadType to its function-name suffix.
PrimaryType
Encoding of the elemental type, for "overloading" @newSparseTensor.
Definition Enums.h:82
RankedTensorType getRankedTensorType(T &&t)
Convenience method to abbreviate casting getType().
bool isValidPrimaryType(Type elemTp)
Returns true if the given type is a valid sparse tensor element type supported by the runtime library...
uint64_t Level
The type of level identifiers and level-ranks.
PrimaryType primaryTypeEncoding(Type elemTp)
Converts a primary storage type to its internal type-encoding.
Operation * getTop(Operation *op)
Scans to top of generated loop.
Value createOrFoldSliceStrideOp(OpBuilder &builder, Location loc, Value tensor, Dimension dim)
Generates code to retrieve the slice slice for the sparse tensor slice, return a constant if the offs...
Type getOpaquePointerType(MLIRContext *ctx)
Returns the equivalent of void* for opaque arguments to the execution engine.
SparseTensorEncodingAttr getSparseTensorEncoding(Type type)
Convenience method to get a sparse encoding attribute from a type.
Value genMapBuffers(OpBuilder &builder, Location loc, SparseTensorType stt, ArrayRef< Value > dimSizesValues, Value dimSizesBuffer, SmallVectorImpl< Value > &lvlSizesValues, Value &dim2lvlBuffer, Value &lvl2dimBuffer)
Generates code to set up the buffer parameters for a map.
Value genIsNonzero(OpBuilder &builder, Location loc, Value v)
Generates the comparison v != 0 where v is of numeric type.
Value genReader(OpBuilder &builder, Location loc, SparseTensorType stt, Value tensor, SmallVectorImpl< Value > &dimSizesValues, Value &dimSizesBuffer)
Generates code that opens a reader and sets the dimension sizes.
Value genScalarToTensor(OpBuilder &builder, Location loc, Value elem, Type dstTp)
Add conversion from scalar to given type (possibly a 0-rank tensor).
void deallocDenseTensor(OpBuilder &builder, Location loc, Value buffer)
Generates code to deallocate a dense buffer.
Value genAlloca(OpBuilder &builder, Location loc, Value sz, Type tp)
Generates an uninitialized temporary buffer of the given size and type, but returns it as type memref...
constexpr uint64_t encodeLvl(uint64_t i, uint64_t c, uint64_t ii)
Definition Enums.h:483
SmallVector< Value > loadAll(OpBuilder &builder, Location loc, size_t size, Value mem, size_t offsetIdx=0, Value offsetVal=Value())
Loads size-many values from the memref, which must have rank-1 and size greater-or-equal to size.
int64_t Size
The type for individual components of a compile-time shape, including the value ShapedType::kDynamic ...
constexpr uint64_t encodeDim(uint64_t i, uint64_t cf, uint64_t cm)
Bit manipulations for affine encoding.
Definition Enums.h:471
void genReshapeDstShape(OpBuilder &builder, Location loc, SmallVectorImpl< Value > &dstShape, ArrayRef< Value > srcShape, ArrayRef< Size > staticDstShape, ArrayRef< ReassociationIndices > reassociation)
Computes the shape of destination tensor of a reshape operator.
SparseTensorType getSparseTensorType(Value val)
Convenience methods to obtain a SparseTensorType from a Value.
void reshapeCvs(OpBuilder &builder, Location loc, ArrayRef< ReassociationIndices > reassociation, ValueRange srcSizes, ValueRange srcCvs, ValueRange dstSizes, SmallVectorImpl< Value > &dstCvs)
Reshape coordinates during a reshaping operation.
func::CallOp createFuncCall(OpBuilder &builder, Location loc, StringRef name, TypeRange resultType, ValueRange operands, EmitCInterface emitCInterface)
Creates a CallOp to the function reference returned by getFunc() in the builder's module.
Value genCast(OpBuilder &builder, Location loc, Value value, Type dstTy)
Add type casting between arith and index types when needed.
StringRef primaryTypeFunctionSuffix(PrimaryType pt)
Convert PrimaryType to its function-name suffix.
Value createOrFoldSliceOffsetOp(OpBuilder &builder, Location loc, Value tensor, Dimension dim)
Generates code to retrieve the slice offset for the sparse tensor slice, return a constant if the off...
Value constantPrimaryTypeEncoding(OpBuilder &builder, Location loc, Type elemTp)
Generates a constant of the internal type-encoding for primary storage.
void sizesFromSrc(OpBuilder &builder, SmallVectorImpl< Value > &sizes, Location loc, Value src)
Populates given sizes array from dense tensor or sparse tensor constant.
Type getOverheadType(Builder &builder, OverheadType ot)
Converts the internal type-encoding for overhead storage to an mlir::Type.
EmitCInterface
Shorthand aliases for the emitCInterface argument to getFunc(), createFuncCall(), and replaceOpWithFu...
Value allocDenseTensor(OpBuilder &builder, Location loc, RankedTensorType tensorTp, ValueRange sizes)
Generates code to allocate a buffer of the given type, and zero initialize it.
void storeAll(OpBuilder &builder, Location loc, Value mem, ValueRange vs, size_t offsetIdx=0, Value offsetVal=Value())
Stores all the values of vs into the memref mem, which must have rank-1 and size greater-or-equal to ...
Include the generated interface declarations.
Value convertScalarToDtype(OpBuilder &b, Location loc, Value operand, Type toType, bool isUnsignedCast)
Converts a scalar value operand to type toType.
Definition Utils.cpp:241
Type getType(OpFoldResult ofr)
Returns the int type of the integer in ofr.
Definition Utils.cpp:307
@ Mul
RHS of mul is always a constant or a symbolic expression.
Definition AffineExpr.h:43
@ Mod
RHS of mod is always a constant or a symbolic expression with a positive value.
Definition AffineExpr.h:46
@ DimId
Dimensional identifier.
Definition AffineExpr.h:59
@ FloorDiv
RHS of floordiv is always a constant or a symbolic expression.
Definition AffineExpr.h:48
std::conditional_t< std::is_same_v< Ty, mlir::Type >, mlir::Value, detail::TypedValue< Ty > > TypedValue
If Ty is mlir::Type this will select Value instead of having a wrapper around it.
Definition Value.h:494
llvm::function_ref< Fn > function_ref
Definition LLVM.h:144
Eliminates variable at the specified position using Fourier-Motzkin variable elimination.