7
7
// ===----------------------------------------------------------------------===//
8
8
9
9
#include " CodegenEnv.h"
10
+
11
+ #include " mlir/Dialect/Bufferization/IR/Bufferization.h"
12
+ #include " mlir/Dialect/Linalg/Utils/Utils.h"
13
+ #include " mlir/Dialect/Tensor/IR/Tensor.h"
10
14
#include < optional>
11
15
12
16
using namespace mlir ;
13
17
using namespace mlir ::sparse_tensor;
14
18
19
+ // ===----------------------------------------------------------------------===//
20
+ // Code generation environment helper functions
21
+ // ===----------------------------------------------------------------------===//
22
+
23
+ // / Returns true if tensor materializes uninitialized into the computation.
24
+ static bool isMaterializing (Value val) {
25
+ return val.getDefiningOp <tensor::EmptyOp>() ||
26
+ val.getDefiningOp <bufferization::AllocTensorOp>();
27
+ }
28
+
15
29
// ===----------------------------------------------------------------------===//
16
30
// Code generation environment constructor and general methods
17
31
// ===----------------------------------------------------------------------===//
@@ -25,11 +39,18 @@ CodegenEnv::CodegenEnv(linalg::GenericOp linop, SparsificationOptions opts,
25
39
expFilled(), expAdded(), expCount(), redVal(), redExp(-1u ),
26
40
redCustom(-1u ) {}
27
41
28
- void CodegenEnv::startEmit (OpOperand *so, unsigned lv) {
29
- assert (sparseOut == nullptr && insChain == nullptr &&
30
- " must only start emitting once" );
31
- sparseOut = so;
32
- outerParNest = lv;
42
+ LogicalResult CodegenEnv::initTensorExp () {
43
+ // Builds the tensor expression for the Linalg operation in SSA form.
44
+ std::optional<unsigned > optExp = latticeMerger.buildTensorExpFromLinalg (op ());
45
+ if (!optExp || !isAdmissibleTensorExp (*optExp))
46
+ return failure ();
47
+
48
+ tensorExp = *optExp;
49
+ return success ();
50
+ }
51
+
52
+ void CodegenEnv::startEmit () {
53
+ assert (insChain == nullptr && " must only start emitting once" );
33
54
if (sparseOut) {
34
55
insChain = sparseOut->get ();
35
56
latticeMerger.setHasSparseOut (true );
@@ -66,6 +87,71 @@ std::optional<Operation *> CodegenEnv::genLoopBoundary(
66
87
return r;
67
88
}
68
89
90
+ // ===----------------------------------------------------------------------===//
91
+ // Code generation environment verify functions.
92
+ // ===----------------------------------------------------------------------===//
93
+
94
+ bool CodegenEnv::isAdmissibleTensorExp (unsigned exp) {
95
+ // We reject any expression that makes a reduction from `-outTensor`, as those
96
+ // expressions create a dependency between the current iteration (i) and the
97
+ // previous iteration (i-1). It would require iterating over the whole
98
+ // coordinate space, which prevent exploiting sparsity for faster code.
99
+ for (utils::IteratorType it : linalgOp.getIteratorTypesArray ()) {
100
+ if (it == utils::IteratorType::reduction) {
101
+ if (latticeMerger.hasNegateOnOut (exp))
102
+ return false ;
103
+ break ;
104
+ }
105
+ }
106
+
107
+ OpOperand *lhs = linalgOp.getDpsInitOperand (0 );
108
+ unsigned tensor = lhs->getOperandNumber ();
109
+ auto enc = getSparseTensorEncoding (lhs->get ().getType ());
110
+ // An non-annotated output tensor is assumed dense, and becomes a random
111
+ // access n-dim memref. Admissible since insertions cannot occur.
112
+ if (!enc || enc.isAllDense ())
113
+ return true ;
114
+
115
+ // A tensor expression with a sparse output tensor that changes its values
116
+ // but not its nonzero structure, an operation called "simply dynamic" in
117
+ // [Bik96,Ch9], is also admissible without special env.
118
+ if (latticeMerger.isSingleCondition (tensor, exp))
119
+ return true ;
120
+
121
+ // Accept "truly dynamic" if the output tensor materializes uninitialized
122
+ // into the computation and insertions occur in lexicographic index order.
123
+ sparseOut = lhs;
124
+ return isMaterializing (lhs->get ());
125
+ }
126
+
127
+ bool CodegenEnv::isAdmissibleTopoOrder () {
128
+ if (!hasSparseOutput ())
129
+ return true ;
130
+
131
+ OpOperand *lhs = linalgOp.getDpsInitOperand (0 );
132
+ // Accept "truly dynamic" if the output tensor materializes uninitialized
133
+ // into the computation and insertions occur in lexicographic index order.
134
+ unsigned nest = 0 ;
135
+ auto iteratorTypes = linalgOp.getIteratorTypesArray ();
136
+ for (unsigned i = 0 , e = latticeMerger.getNumLoops (); i < e; i++) {
137
+ if (!latticeMerger.isFilterLoop (topSortAt (i))) {
138
+ // We only count non-filter loops as filter loops should be considered
139
+ // as a special type of parallel loops.
140
+ if (linalg::isReductionIterator (iteratorTypes[topSortAt (i)]))
141
+ break ; // terminate at first reduction
142
+ nest++;
143
+ }
144
+ }
145
+ // Determine admissible dynamic insertion situations:
146
+ // (1) fully injective, since there are no reductions,
147
+ // (2) admissible 1-d expansion in innermost dimension.
148
+ if (nest >= linalgOp.getRank (lhs) - 1 ) {
149
+ outerParNest = nest;
150
+ return true ;
151
+ }
152
+ return false ;
153
+ }
154
+
69
155
// ===----------------------------------------------------------------------===//
70
156
// Code generation environment topological sort methods
71
157
// ===----------------------------------------------------------------------===//
0 commit comments