Change AffineApplyOp to produce a single result, simplifying the code that

works with it, and updating the g3docs.

PiperOrigin-RevId: 231120927
This commit is contained in:
Chris Lattner 2019-01-27 09:33:19 -08:00 committed by jpienaar
parent 36babbd781
commit b42bea215a
18 changed files with 100 additions and 145 deletions

View File

@ -252,7 +252,7 @@ loops and if instructions), the result of a
[`affine_apply`](#'affine_apply'-operation) operation that recursively takes as
arguments any symbolic identifiers. Dimensions may be bound not only to anything
that a symbol is bound to, but also to induction variables of enclosing
[for instructions](#'for'-instruction), and the results of an
[for instructions](#'for'-instruction), and the result of an
[`affine_apply` operation](#'affine_apply'-operation) (which recursively may use
other dimensions and symbols).
@ -1038,15 +1038,15 @@ nullary mapping function that returns the constant value (e.g. `()->(-42)()`).
Example showing reverse iteration of the inner loop:
```mlir {.mlir}
#map57 = (d0, d1)[s0] -> (d0, s0 - d1 - 1)
#map57 = (d0)[s0] -> (s0 - d0 - 1)
func @simple_example(%A: memref<?x?xf32>, %B: memref<?x?xf32>) {
%N = dim %A, 0 : memref<?x?xf32>
for %i = 0 to %N step 1 {
for %j = 0 to %N { // implicitly steps by 1
%0 = affine_apply #map57(%i, %j)[%N]
%tmp = call @F1(%A, %0#0, %0#1) : (memref<?x?xf32>, index, index)->(f32)
call @F2(%tmp, %B, %0#0, %0#1) : (f32, memref<?x?xf32>, index, index)->()
%0 = affine_apply #map57(%j)[%N]
%tmp = call @F1(%A, %i, %0) : (memref<?x?xf32>, index, index)->(f32)
call @F2(%tmp, %B, %i, %0) : (f32, memref<?x?xf32>, index, index)->()
}
}
return
@ -1085,11 +1085,11 @@ Example:
func @reduced_domain_example(%A, %X, %N) : (memref<10xi32>, i32, i32) {
for %i = 0 to %N {
for %j = 0 to %N {
%0 = affine_apply #map42(%i, %j)
%tmp = call @S1(%X, %0#0, %0#1)
%0 = affine_apply #map42(%j)
%tmp = call @S1(%X, %i, %0)
if #set(%i, %j)[%N] {
%1 = affine_apply #map43(%i, %j)
call @S2(%tmp, %A, %1#0, %1#1)
call @S2(%tmp, %A, %i, %1)
}
}
}
@ -1242,17 +1242,16 @@ operation ::= ssa-id `=` `affine_apply` affine-map dim-and-symbol-use-list
```
The `affine_apply` instruction applies an [affine mapping](#affine-expressions)
to a list of SSA values, yielding another list of SSA values. The number of
dimension and symbol arguments to affine_apply must be equal to the respective
number of dimensional and symbolic inputs to the affine mapping, and the number
of results is the dimensionality of the range of the affine mapping. The input
SSA values must all have 'index' type, and the results are all of 'index' type.
to a list of SSA values, yielding a single SSA value. The number of dimension
and symbol arguments to affine_apply must be equal to the respective number of
dimensional and symbolic inputs to the affine mapping; the `affine_apply`
instruction always returns one value. The input operands and result must all
have 'index' type.
Example:
```mlir {.mlir}
#map10 = (d0, d1) -> (floordiv(d0,8), floordiv(d1,128),
d0 mod 8, d1 mod 128)
#map10 = (d0, d1) -> (floordiv(d0,8) + floordiv(d1,128))
...
%1 = affine_apply #map10 (%s, %t)
@ -1572,34 +1571,33 @@ The arity of indices is the rank of the memref (i.e., if the memref loaded from
is of rank 3, then 3 indices are required for the load following the memref
identifier).
In an ML Function, the indices of a load are restricted to SSA values bound to
surrounding loop induction variables, [symbols](#dimensions-and-symbols),
results of a [`constant` operation](#'constant'-operation), or the results of an
In an `if` or `for` body, the indices of a load are restricted to SSA values
bound to surrounding loop induction variables,
[symbols](#dimensions-and-symbols), results of a
[`constant` operation](#'constant'-operation), or the result of an
`affine_apply` operation that can in turn take as arguments all of the
aforementioned SSA values or the recursively results of such an `affine_apply`
aforementioned SSA values or the recursively result of such an `affine_apply`
operation.
Example:
```mlir {.mlir}
#remap1 = (d0, d1) -> (3*d0, d1+1)
#remap2 = (d0) -> (2*d0 + 1)
...
%1 = affine_apply #remap1(%i, %j)
%12 = load %A[%1#0, %1#1] : memref<8x?xi32, #layout, hbm>
%1 = affine_apply (d0, d1) -> (3*d0) (%i, %j)
%2 = affine_apply (d0, d1) -> (d1+1) (%i, %j)
%12 = load %A[%1, %2] : memref<8x?xi32, #layout, hbm>
// Example of an indirect load (treated as non-affine)
%2 = affine_apply #remap2(%12)
%13 = load %A[%2, %1#1] : memref<4x?xi32, #layout, hbm>
%3 = affine_apply (d0) -> (2*d0 + 1)(%12)
%13 = load %A[%3, %2] : memref<4x?xi32, #layout, hbm>
```
**Context:** The `load` and `store` instructions are specifically crafted to
fully resolve a reference to an element of a memref, and (in an ML function) the
compiler can follow use-def chains (e.g. through
fully resolve a reference to an element of a memref, and (in polyhedral `if` and
`for` instructions) the compiler can follow use-def chains (e.g. through
[`affine_apply`](#'affine_apply'-operation) operations) to precisely analyze
references at compile-time using polyhedral techniques. This is possible because
of the [restrictions on dimensions and symbols](#dimensions-and-symbols) in ML
functions.
of the [restrictions on dimensions and symbols](#dimensions-and-symbols) in
these contexts.
#### 'store' operation {#'store'-operation}
@ -1616,10 +1614,10 @@ provided within brackets need to match the rank of the memref.
In an ML Function, the indices of a store are restricted to SSA values bound to
surrounding loop induction variables, [symbols](#dimensions-and-symbols),
results of a [`constant` operation](#'constant'-operation), or the results of an
results of a [`constant` operation](#'constant'-operation), or the result of an
[`affine_apply`](#'affine_apply'-operation) operation that can in turn take as
arguments all of the aforementioned SSA values or the recursively results of
such an `affine_apply` operation.
arguments all of the aforementioned SSA values or the recursively result of such
an `affine_apply` operation.
Example:
@ -1628,12 +1626,12 @@ store %100, %A[%1, 1023] : memref<4x?xf32, #layout, hbm>
```
**Context:** The `load` and `store` instructions are specifically crafted to
fully resolve a reference to a scalar member of a memref, and (in an ML
function) the compiler can follow use-def chains (e.g. through
fully resolve a reference to an element of a memref, and (in polyhedral `if` and
`for` instructions) the compiler can follow use-def chains (e.g. through
[`affine_apply`](#'affine_apply'-operation) operations) to precisely analyze
references at compile-time using polyhedral techniques. This is possible because
of the [restrictions on dimensions and symbols](#dimensions-and-symbols) in ML
functions.
of the [restrictions on dimensions and symbols](#dimensions-and-symbols) in
these contexts.
#### 'tensor_load' operation {#'tensor_load'-operation}

View File

@ -116,10 +116,10 @@ n-ranked tensor. This disallows the equivalent of pointer arithmetic or the
ability to index into the same memref in other ways (something which C arrays
allow for example). Furthermore, in an affine constructs, the compiler can
follow use-def chains (e.g. through
[affine_apply instructions](https://docs.google.com/document/d/1lwJ3o6MrkAa-jiqEwoORBLW3bAI1f4ONofjRqMK1-YU/edit?ts=5b208818#heading=h.kt8lzanb487r))
to precisely analyze references at compile-time using polyhedral techniques.
This is possible because of the
[restrictions on dimensions and symbols](https://docs.google.com/document/d/1lwJ3o6MrkAa-jiqEwoORBLW3bAI1f4ONofjRqMK1-YU/edit?ts=5b208818#heading=h.fnmv1awabfj).
[affine_apply instructions](LangRef.md#'affine_apply'-operation)) to precisely
analyze references at compile-time using polyhedral techniques. This is possible
because of the
[restrictions on dimensions and symbols](LangRef.md#dimensions-and-symbols).
A scalar of element-type (a primitive type or a vector type) that is stored in
memory is modeled as a 0-d memref. This is also necessary for scalars that are
@ -634,12 +634,13 @@ in a dilated convolution.
// S4 = h_pad_low, S5 = w_pad_low
// %out0 = %0#1 * %h_stride + %0#4 * %h_kernel_dilation - %h_pad_low
// %out1= %0#2 * %w_stride + %0#5 * %w_kernel_dilation - %w_pad_low
#map1 = (d0, d1, d2, d3) [S0, S1, S2, S3, S4, S5] -> (d0 * S0 + d2 * S2 - %S4,
d1 * S1 + d3 * S3 - %S5)
#map1_0 = (d0, d1, d2, d3) [S0, S1, S2, S3, S4, S5] -> (d0 * S0 + d2 * S2 - %S4)
#map1_1 = (d0, d1, d2, d3) [S0, S1, S2, S3, S4, S5] -> (d1 * S1 + d3 * S3 - %S5)
// Semi-affine map to undilated input coordinate space.
// d0 = input_h, d1 = input_w, S0 = h_base_dilation, S1 = w_base_dilation.
#map2 = (d0, d1) [S0, S1] -> (d0 / S0, d1 / S1)
#map2_0 = (d0, d1) [S0, S1] -> (d0 / S0)
#map2_1 = (d0, d1) [S0, S1] -> (d1 / S1)
// Conv2D shapes:
// input: [batch, input_height, input_width, input_feature]
@ -655,19 +656,21 @@ func @conv2d(memref<16x1024x1024x3xf32, #lm0, vmem> %input,
for %kh = 0 to %kernel_height {
for %kw = 0 to %kernel_width {
for %if = 0 to %input_feature {
%0 = affine_apply #map0 (%b, %oh, %ow, %of, %kh, %kw, %if)
// Calculate input indices.
%1 = affine_apply #map1 (%0#1, %0#2, %0#4, %0#5)
%1_0 = affine_apply #map1_0 (%0#1, %0#2, %0#4, %0#5)
[%h_stride, %w_stride, %h_kernel_dilation, %w_kernel_dilation,
%h_pad_low, %w_pad_low]
%1_1 = affine_apply #map1_1 (%0#1, %0#2, %0#4, %0#5)
[%h_stride, %w_stride, %h_kernel_dilation, %w_kernel_dilation,
%h_pad_low, %w_pad_low]
// Check if access is not in padding.
if #domain(%1#0, %1#1)
if #domain(%1_0, %1_1)
[%h_base_dilation, %w_kernel_dilation, %h_bound, %w_bound] {
%2 = affine_apply #map2 (%1#0, %1#1)
%2_0 = affine_apply #map2 (%1_0, %1_1)
%2_1 = affine_apply #map2 (%1_0, %1_1)
// Compute: output[output_indices] += input[input_indices] * kernel[kernel_indices]
call @multiply_accumulate(%input, %kernel, %output, %0#0, %0#1, %0#2, %0#3,
%0#4, %0#5, %0#6, %2#0, %2#1)
call @multiply_accumulate(%input, %kernel, %output, %b, %oh, %ow, %of, %kh, %kw, %if, %2_0, %2_1)
}
}
}

View File

@ -37,10 +37,10 @@ public:
};
/// The "affine_apply" operation applies an affine map to a list of operands,
/// yielding a list of results. The operand and result list sizes must be the
/// same. All operands and results are of type 'Index'. This operation
/// requires a single affine map attribute named "map".
/// For example:
/// yielding a single result. The operand list must be the same size as the
/// number of arguments to the affine mapping. All operands and the result are
/// of type 'Index'. This operation requires a single affine map attribute named
/// "map". For example:
///
/// %y = "affine_apply" (%x) { map: (d0) -> (d0 + 1) } :
/// (index) -> (index)
@ -50,9 +50,8 @@ public:
/// #map42 = (d0)->(d0+1)
/// %y = affine_apply #map42(%x)
///
class AffineApplyOp
: public Op<AffineApplyOp, OpTrait::VariadicOperands,
OpTrait::VariadicResults, OpTrait::HasNoSideEffect> {
class AffineApplyOp : public Op<AffineApplyOp, OpTrait::VariadicOperands,
OpTrait::OneResult, OpTrait::HasNoSideEffect> {
public:
/// Builds an affine apply op with the specified map and operands.
static void build(Builder *builder, OperationState *result, AffineMap map,
@ -75,8 +74,7 @@ public:
static bool parse(OpAsmParser *parser, OperationState *result);
void print(OpAsmPrinter *p) const;
bool verify() const;
bool constantFold(ArrayRef<Attribute> operandConstants,
SmallVectorImpl<Attribute> &results,
Attribute constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const;
static void getCanonicalizationPatterns(OwningRewritePatternList &results,

View File

@ -1474,17 +1474,6 @@ AffineMap AffineApplyNormalizer::renumber(const AffineApplyOp &app) {
return renumber(normalizer);
}
static unsigned getIndexOf(Value *v, const AffineApplyOp &op) {
unsigned numResults = op.getNumResults();
for (unsigned i = 0; i < numResults; ++i) {
if (v == op.getResult(i)) {
return i;
}
}
llvm_unreachable("value is not a result of AffineApply");
return static_cast<unsigned>(-1);
}
AffineApplyNormalizer::AffineApplyNormalizer(AffineMap map,
ArrayRef<Value *> operands)
: AffineApplyNormalizer() {
@ -1511,9 +1500,8 @@ AffineApplyNormalizer::AffineApplyNormalizer(AffineMap map,
} else {
auto *inst = t->getDefiningInst();
auto app = inst->dyn_cast<AffineApplyOp>();
unsigned idx = getIndexOf(t, *app);
auto tmpMap = renumber(*app);
exprs.push_back(tmpMap.getResult(idx));
exprs.push_back(tmpMap.getResult(0));
}
}

View File

@ -105,8 +105,7 @@ AffineValueMap::AffineValueMap(const AffineApplyOp &op)
: map(op.getAffineMap()) {
for (auto *operand : op.getOperands())
operands.push_back(const_cast<Value *>(operand));
for (unsigned i = 0, e = op.getNumResults(); i < e; i++)
results.push_back(const_cast<Value *>(op.getResult(i)));
results.push_back(const_cast<Value *>(op.getResult()));
}
AffineValueMap::AffineValueMap(AffineMap map, ArrayRef<Value *> operands)

View File

@ -145,17 +145,7 @@ bool mlir::isAccessInvariant(const Value &iv, const Value &index) {
auto composeOp = affineApplyOps[0]->cast<AffineApplyOp>();
// We need yet another level of indirection because the `dim` index of the
// access may not correspond to the `dim` index of composeOp.
unsigned idx = std::numeric_limits<unsigned>::max();
unsigned numResults = composeOp->getNumResults();
for (unsigned i = 0; i < numResults; ++i) {
if (&index == composeOp->getResult(i)) {
idx = i;
break;
}
}
assert(idx < std::numeric_limits<unsigned>::max());
return !AffineValueMap(*composeOp)
.isFunctionOf(idx, &const_cast<Value &>(iv));
return !AffineValueMap(*composeOp).isFunctionOf(0, const_cast<Value *>(&iv));
}
llvm::DenseSet<const Value *>

View File

@ -95,8 +95,7 @@ Value *add(FuncBuilder *builder, Location location, Value *a, Value *b) {
auto d0 = getAffineDimExpr(0, context);
auto d1 = getAffineDimExpr(1, context);
auto map = AffineMap::get(2, 0, {d0 + d1}, {});
return makeComposedAffineApply(builder, location, map, {a, b})
->getResult(0);
return makeComposedAffineApply(builder, location, map, {a, b});
} else if (isIntElement(*a)) {
return builder->create<AddIOp>(location, a, b)->getResult();
}
@ -110,8 +109,7 @@ Value *sub(FuncBuilder *builder, Location location, Value *a, Value *b) {
auto d0 = getAffineDimExpr(0, context);
auto d1 = getAffineDimExpr(1, context);
auto map = AffineMap::get(2, 0, {d0 - d1}, {});
return makeComposedAffineApply(builder, location, map, {a, b})
->getResult(0);
return makeComposedAffineApply(builder, location, map, {a, b});
} else if (isIntElement(*a)) {
return builder->create<SubIOp>(location, a, b)->getResult();
}

View File

@ -119,9 +119,6 @@ void AffineApplyOp::print(OpAsmPrinter *p) const {
}
bool AffineApplyOp::verify() const {
if (getNumResults() != 1)
return emitOpError("multi-result affine_apply is not supported");
// Check that affine map attribute was specified.
auto affineMapAttr = getAttrOfType<AffineMapAttr>("map");
if (!affineMapAttr)
@ -136,8 +133,8 @@ bool AffineApplyOp::verify() const {
"operand count and affine map dimension and symbol count must match");
// Verify that result count matches affine map result count.
if (getNumResults() != map.getNumResults())
return emitOpError("result count and affine map result count must match");
if (map.getNumResults() != 1)
return emitOpError("mapping must produce one value");
return false;
}
@ -163,14 +160,13 @@ bool AffineApplyOp::isValidSymbol() const {
return true;
}
bool AffineApplyOp::constantFold(ArrayRef<Attribute> operandConstants,
SmallVectorImpl<Attribute> &results,
Attribute AffineApplyOp::constantFold(ArrayRef<Attribute> operands,
MLIRContext *context) const {
auto map = getAffineMap();
if (map.constantFold(operandConstants, results))
return true;
// Return false on success.
return false;
SmallVector<Attribute, 1> result;
if (map.constantFold(operands, result))
return Attribute();
return result[0];
}
namespace {

View File

@ -78,10 +78,7 @@ PassResult ComposeAffineMaps::runOnFunction(Function *f) {
FuncBuilder b(m.first);
auto newApp = makeComposedAffineApply(&b, app->getLoc(),
app->getAffineMap(), operands);
unsigned idx = 0;
for (auto *v : app->getResults()) {
v->replaceAllUsesWith(newApp->getResult(idx++));
}
app->replaceAllUsesWith(newApp);
}
{
auto pattern = Op(affineApplyOp);
@ -89,9 +86,7 @@ PassResult ComposeAffineMaps::runOnFunction(Function *f) {
std::reverse(apps.begin(), apps.end());
for (auto m : apps) {
auto app = cast<OperationInst>(m.first)->cast<AffineApplyOp>();
bool hasNonEmptyUse = llvm::any_of(
app->getResults(), [](Value *r) { return !r->use_empty(); });
if (!hasNonEmptyUse) {
if (app->use_empty()) {
m.first->erase();
}
}

View File

@ -280,8 +280,7 @@ bool DmaGeneration::generateDma(const MemRefRegion &region, ForInst *forInst,
// corresponding dimension on the memory region (stored in 'offset').
auto map = top.getAffineMap(
cst->getNumDimIds() + cst->getNumSymbolIds() - rank, 0, offset, {});
memIndices.push_back(
b->create<AffineApplyOp>(loc, map, outerIVs)->getResult(0));
memIndices.push_back(b->create<AffineApplyOp>(loc, map, outerIVs));
}
// The fast buffer is DMAed into at location zero; addressing is relative.
bufIndices.push_back(zeroIndex);

View File

@ -231,10 +231,8 @@ bool mlir::loopUnrollJamByFactor(ForInst *forInst, uint64_t unrollJamFactor) {
// iv' = iv + i, i = 1 to unrollJamFactor-1.
auto d0 = builder.getAffineDimExpr(0);
auto bumpMap = builder.getAffineMap(1, 0, {d0 + i * step}, {});
auto *ivUnroll =
builder
.create<AffineApplyOp>(forInst->getLoc(), bumpMap, forInstIV)
->getResult(0);
auto ivUnroll = builder.create<AffineApplyOp>(forInst->getLoc(),
bumpMap, forInstIV);
operandMapping.map(forInstIV, ivUnroll);
}
// Clone the sub-block being unroll-jammed.

View File

@ -562,13 +562,12 @@ bool LowerAffinePass::lowerAffineApply(AffineApplyOp *op) {
llvm::to_vector<8>(op->getOperands()));
if (!maybeExpandedMap)
return true;
for (auto pair : llvm::zip(op->getResults(), *maybeExpandedMap)) {
Value *original = std::get<0>(pair);
Value *expanded = std::get<1>(pair);
Value *original = op->getResult();
Value *expanded = (*maybeExpandedMap)[0];
if (!expanded)
return true;
original->replaceAllUsesWith(expanded);
}
op->erase();
return false;
}

View File

@ -376,11 +376,10 @@ reindexAffineIndices(FuncBuilder *b, VectorType hwVectorType,
// Create a bunch of single result maps.
return functional::map(
[b, numIndices, memrefIndices](AffineExpr expr) {
[b, numIndices, memrefIndices](AffineExpr expr) -> Value * {
auto map = AffineMap::get(numIndices, 0, expr, {});
auto app = makeComposedAffineApply(b, b->getInsertionPoint()->getLoc(),
map, memrefIndices);
return app->getResult(0);
return makeComposedAffineApply(b, b->getInsertionPoint()->getLoc(), map,
memrefIndices);
},
affineExprs);
}

View File

@ -126,9 +126,8 @@ static bool doubleBuffer(Value *oldMemRef, ForInst *forInst) {
// replaceAllMemRefUsesWith will always succeed unless the forInst body has
// non-deferencing uses of the memref.
if (!replaceAllMemRefUsesWith(oldMemRef, newMemRef, ivModTwoOp->getResult(0),
AffineMap(), {},
&*forInst->getBody()->begin())) {
if (!replaceAllMemRefUsesWith(oldMemRef, newMemRef, {ivModTwoOp}, AffineMap(),
{}, &*forInst->getBody()->begin())) {
LLVM_DEBUG(llvm::dbgs()
<< "memref replacement for double buffering failed\n";);
ivModTwoOp->getInstruction()->erase();

View File

@ -117,7 +117,7 @@ bool mlir::promoteIfSingleIteration(ForInst *forInst) {
} else {
auto affineApplyOp = builder.create<AffineApplyOp>(
forInst->getLoc(), lb.getMap(), lbOperands);
iv->replaceAllUsesWith(affineApplyOp->getResult(0));
iv->replaceAllUsesWith(affineApplyOp);
}
}
}
@ -177,12 +177,11 @@ generateLoop(AffineMap lbMap, AffineMap ubMap,
// shift.
if (!srcIV->use_empty() && shift != 0) {
auto b = FuncBuilder::getForInstBodyBuilder(loopChunk);
auto *ivRemap = b.create<AffineApplyOp>(
auto ivRemap = b.create<AffineApplyOp>(
srcForInst->getLoc(),
b.getSingleDimShiftAffineMap(-static_cast<int64_t>(
srcForInst->getStep() * shift)),
loopChunkIV)
->getResult(0);
b.getSingleDimShiftAffineMap(
-static_cast<int64_t>(srcForInst->getStep() * shift)),
loopChunkIV);
operandMap.map(srcIV, ivRemap);
} else {
operandMap.map(srcIV, loopChunkIV);
@ -432,9 +431,8 @@ bool mlir::loopUnrollByFactor(ForInst *forInst, uint64_t unrollFactor) {
// iv' = iv + 1/2/3...unrollFactor-1;
auto d0 = builder.getAffineDimExpr(0);
auto bumpMap = builder.getAffineMap(1, 0, {d0 + i * step}, {});
auto *ivUnroll =
builder.create<AffineApplyOp>(forInst->getLoc(), bumpMap, forInstIV)
->getResult(0);
auto ivUnroll =
builder.create<AffineApplyOp>(forInst->getLoc(), bumpMap, forInstIV);
operandMap.map(forInstIV, ivUnroll);
}

View File

@ -136,7 +136,7 @@ bool mlir::replaceAllMemRefUsesWith(const Value *oldMemRef, Value *newMemRef,
indexRemap.getNumSymbols(), resultExpr, {});
auto afOp = builder.create<AffineApplyOp>(opInst->getLoc(),
singleResMap, remapOperands);
state.operands.push_back(afOp->getResult(0));
state.operands.push_back(afOp);
}
} else {
// No remapping specified.
@ -266,7 +266,7 @@ void mlir::createAffineComputationSlice(
break;
}
if (j < subOperands.size()) {
newOperands[i] = (*sliceOps)[j]->getResult(0);
newOperands[i] = (*sliceOps)[j];
}
}
for (unsigned idx = 0, e = newOperands.size(); idx < e; idx++) {

View File

@ -240,9 +240,7 @@ static bool affineApplyOp(const Instruction &inst) {
static bool singleResultAffineApplyOpWithoutUses(const Instruction &inst) {
const auto &opInst = cast<OperationInst>(inst);
auto app = opInst.dyn_cast<AffineApplyOp>();
return app && (app->getNumResults() == 1) &&
app->getResult(0)->getUses().end() ==
app->getResult(0)->getUses().begin();
return app && app->use_empty();
}
void VectorizerTestPass::testNormalizeMaps(Function *f) {

View File

@ -62,7 +62,7 @@ func @affine_apply_wrong_result_count() {
^bb0:
%i = "constant"() {value: 0} : () -> index
%j = "constant"() {value: 1} : () -> index
%x = "affine_apply" (%i, %j) {map: (d0, d1) -> ((d0 + 1), (d1 + 2))} : (index,index) -> (index) // expected-error {{'affine_apply' op result count and affine map result count must match}}
%x = "affine_apply" (%i, %j) {map: (d0, d1) -> ((d0 + 1), (d1 + 2))} : (index,index) -> (index) // expected-error {{'affine_apply' op mapping must produce one value}}
return
}