forked from OSchip/llvm-project
[mlir][linalg] Purge linalg.indexed_generic.
Differential Revision: https://reviews.llvm.org/D104449
This commit is contained in:
parent
aa6e8e9572
commit
5b3cb31edb
|
@ -32,7 +32,7 @@ class LinalgStructuredBase_Op<string mnemonic, list<OpTrait> props>
|
||||||
// always be 0 for index-free linalg ops. For IndexedGeneric, this must be
|
// always be 0 for index-free linalg ops. For IndexedGeneric, this must be
|
||||||
// equal to numLoops.
|
// equal to numLoops.
|
||||||
unsigned getNumPayloadInductionVariables() {
|
unsigned getNumPayloadInductionVariables() {
|
||||||
return isa<IndexedGenericOp>(this->getOperation()) ? getNumLoops() : 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Return whether the op accesses the iteration indices.
|
// Return whether the op accesses the iteration indices.
|
||||||
|
@ -671,140 +671,6 @@ def GenericOp : GenericOpBase<"generic"> {
|
||||||
let hasFolder = 1;
|
let hasFolder = 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// GenericOp with Indexing (i.e. multi-for style in which the region is passed
|
|
||||||
/// the enclosing loop induction variables)
|
|
||||||
def IndexedGenericOp : GenericOpBase<"indexed_generic"> {
|
|
||||||
let description = [{
|
|
||||||
Indexed Generic Linalg op form where the key properties of the computation
|
|
||||||
are specified as attributes. In pretty form, a `linalg.indexed_generic` op
|
|
||||||
is written as:
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
linalg.indexed_generic #trait_attribute
|
|
||||||
ins(%A, %B : memref<?x?xf32, stride_specification>,
|
|
||||||
memref<?x?xf32, stride_specification>)
|
|
||||||
outs(%C : memref<?x?xf32, stride_specification>)
|
|
||||||
attrs = {other-optional-attributes}
|
|
||||||
{region}
|
|
||||||
```
|
|
||||||
|
|
||||||
Where #trait_attributes is an alias of a dictionary attribute containing:
|
|
||||||
- doc [optional]: a documentation string
|
|
||||||
- indexing_maps: a list of AffineMapAttr, one AffineMapAttr per each input
|
|
||||||
and output view. Such AffineMapAttr specifies the mapping between the
|
|
||||||
loops and the indexing within each view.
|
|
||||||
- library_call [optional]: a StringAttr containing the name of an
|
|
||||||
external library function that the linalg.indexed_generic operation
|
|
||||||
maps to. The external library is assumed to be dynamically linked and
|
|
||||||
no strong compile-time guarantees are provided. In the absence of such
|
|
||||||
a library call, linalg.indexed_generic will always lower to loops.
|
|
||||||
- iterator_types: an ArrayAttr they type of the enclosing loops; Each
|
|
||||||
element of the list represents and iterator of one of the following
|
|
||||||
types:
|
|
||||||
parallel, reduction, window
|
|
||||||
|
|
||||||
Example:
|
|
||||||
Defining a #matmul_trait attribute in MLIR can be done as follows:
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
#matmul_accesses = [
|
|
||||||
(m, n, k) -> (m, k),
|
|
||||||
(m, n, k) -> (k, n),
|
|
||||||
(m, n, k) -> (m, n)
|
|
||||||
]
|
|
||||||
#matmul_trait = {
|
|
||||||
doc = "C(m, n) += A(m, k) * B(k, n)",
|
|
||||||
indexing_maps = #matmul_accesses,
|
|
||||||
library_call = "linalg_matmul",
|
|
||||||
iterator_types = ["parallel", "parallel", "reduction"]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
And can be reused in multiple places as:
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
linalg.indexed_generic #matmul_trait
|
|
||||||
ins(%A, %B : memref<?x?xf32, stride_specification>,
|
|
||||||
memref<?x?xf32, stride_specification>)
|
|
||||||
outs(%C : memref<?x?xf32, stride_specification>) {
|
|
||||||
(%offset_m: index, %offset_n: index, %offset_k: index,
|
|
||||||
%a: f32, %b: f32, %c: f32) :
|
|
||||||
"some_optional_computation"(%offset_m, %offset_n, %offset_k)
|
|
||||||
%d = mulf %a, %b: f32
|
|
||||||
%e = addf %c, %d: f32
|
|
||||||
linalg_yield %e : f32
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
This may lower to either:
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
call @linalg_matmul(%offset_m, %offset_n, %offset_k, %A, %B, %C) :
|
|
||||||
(index, index, index,
|
|
||||||
memref<?x?xf32, stride_specification>,
|
|
||||||
memref<?x?xf32, stride_specification>,
|
|
||||||
memref<?x?xf32, stride_specification>)
|
|
||||||
-> ()
|
|
||||||
```
|
|
||||||
|
|
||||||
or IR resembling:
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
scf.for %m = %c0 to %M step %c1 {
|
|
||||||
scf.for %n = %c0 to %N step %c1 {
|
|
||||||
scf.for %k = %c0 to %K step %c1 {
|
|
||||||
%a = load %A[%m, %k] : memref<?x?xf32, stride_specification>
|
|
||||||
%b = load %B[%k, %n] : memref<?x?xf32, stride_specification>
|
|
||||||
%c = load %C[%m, %n] : memref<?x?xf32, stride_specification>
|
|
||||||
"some_optional_computation"(%m, %n, %k)
|
|
||||||
%d = mulf %a, %b: f32
|
|
||||||
%e = addf %c, %d: f32
|
|
||||||
store %d, %C[%m, %n] : memref<?x?x?xf32, stride_specification>
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
To allow progressive lowering from the value world (a.k.a tensor values) to
|
|
||||||
the buffer world (a.k.a memref values), a `linalg.indexed_generic` op
|
|
||||||
allows mixing tensors and buffers operands and tensor results.
|
|
||||||
|
|
||||||
```mlir
|
|
||||||
%C = linalg.indexed_generic #trait_attribute
|
|
||||||
ins(%A, %B : tensor<?x?xf32>, memref<?x?xf32, stride_specification>)
|
|
||||||
outs(%C : tensor<?x?xf32>)
|
|
||||||
{other-optional-attributes}
|
|
||||||
{region_with_index_arguments}
|
|
||||||
-> (tensor<?x?xf32>)
|
|
||||||
```
|
|
||||||
}];
|
|
||||||
|
|
||||||
let builders = [
|
|
||||||
OpBuilder<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
|
|
||||||
"ValueRange":$outputs, "ArrayRef<AffineMap>":$indexingMaps,
|
|
||||||
"ArrayRef<StringRef>":$iteratorTypes, "StringRef":$doc,
|
|
||||||
"StringRef":$libraryCall,
|
|
||||||
CArg<"function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>",
|
|
||||||
"nullptr">)>,
|
|
||||||
OpBuilder<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
|
|
||||||
"ArrayRef<AffineMap>":$indexingMaps, "ArrayRef<StringRef>":$iteratorTypes,
|
|
||||||
"StringRef":$doc, "StringRef":$libraryCall,
|
|
||||||
CArg<"function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>",
|
|
||||||
"nullptr">)>,
|
|
||||||
OpBuilder<(ins "TypeRange":$resultTensorTypes, "ValueRange":$inputs,
|
|
||||||
"ValueRange":$outputs, "ArrayRef<AffineMap>":$indexingMaps,
|
|
||||||
"ArrayRef<StringRef>":$iteratorTypes,
|
|
||||||
CArg<"function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>",
|
|
||||||
"nullptr">)>,
|
|
||||||
OpBuilder<(ins "ValueRange":$inputs, "ValueRange":$outputBuffers,
|
|
||||||
"ArrayRef<AffineMap>":$indexingMaps, "ArrayRef<StringRef>":$iteratorTypes,
|
|
||||||
CArg<"function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>",
|
|
||||||
"nullptr">)>
|
|
||||||
];
|
|
||||||
let verifier = [{ return ::verify(*this); }];
|
|
||||||
|
|
||||||
let hasCanonicalizer = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// Named Linalg ops, implemented as a declarative configurations of generic ops.
|
// Named Linalg ops, implemented as a declarative configurations of generic ops.
|
||||||
|
|
|
@ -100,10 +100,6 @@ LogicalResult mlir::linalg::LinalgOpToLibraryCallRewrite::matchAndRewrite(
|
||||||
if (isa<CopyOp>(op))
|
if (isa<CopyOp>(op))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
// Canonicalize indexed generic operations before library call conversion.
|
|
||||||
if (isa<IndexedGenericOp>(op))
|
|
||||||
return failure();
|
|
||||||
|
|
||||||
auto libraryCallName = getLibraryCallSymbolRef(op, rewriter);
|
auto libraryCallName = getLibraryCallSymbolRef(op, rewriter);
|
||||||
if (!libraryCallName)
|
if (!libraryCallName)
|
||||||
return failure();
|
return failure();
|
||||||
|
|
|
@ -525,69 +525,8 @@ void GenericOp::build(
|
||||||
/*doc=*/"",
|
/*doc=*/"",
|
||||||
/*libraryCall=*/"", bodyBuild);
|
/*libraryCall=*/"", bodyBuild);
|
||||||
}
|
}
|
||||||
void IndexedGenericOp::build(
|
|
||||||
OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
|
|
||||||
ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
|
|
||||||
ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
|
|
||||||
function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
|
|
||||||
bodyBuild) {
|
|
||||||
build(builder, result, resultTensorTypes, inputs, outputs,
|
|
||||||
builder.getAffineMapArrayAttr(indexingMaps),
|
|
||||||
builder.getStrArrayAttr(iteratorTypes),
|
|
||||||
doc.empty() ? StringAttr() : builder.getStringAttr(doc),
|
|
||||||
libraryCall.empty() ? StringAttr()
|
|
||||||
: builder.getStringAttr(libraryCall));
|
|
||||||
if (!bodyBuild)
|
|
||||||
return;
|
|
||||||
|
|
||||||
unsigned nLoops = iteratorTypes.size();
|
static void print(OpAsmPrinter &p, GenericOp op) {
|
||||||
SmallVector<Type, 4> blockArgTypes(nLoops, builder.getIndexType());
|
|
||||||
for (ValueRange container : {inputs, outputs})
|
|
||||||
for (Value v : container)
|
|
||||||
blockArgTypes.push_back(getElementTypeOrSelf(v));
|
|
||||||
|
|
||||||
OpBuilder::InsertionGuard guard(builder);
|
|
||||||
auto ®ion = *result.regions.front();
|
|
||||||
Block *bodyBlock = builder.createBlock(®ion, region.end(), blockArgTypes);
|
|
||||||
bodyBuild(builder, result.location,
|
|
||||||
bodyBlock->getArguments().take_front(nLoops),
|
|
||||||
bodyBlock->getArguments().drop_front(nLoops));
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexedGenericOp::build(
|
|
||||||
OpBuilder &builder, OperationState &result, ValueRange inputs,
|
|
||||||
ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
|
|
||||||
ArrayRef<StringRef> iteratorTypes, StringRef doc, StringRef libraryCall,
|
|
||||||
function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
|
|
||||||
bodyBuild) {
|
|
||||||
build(builder, result, TypeRange{}, inputs, outputs, indexingMaps,
|
|
||||||
iteratorTypes, doc, libraryCall, bodyBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexedGenericOp::build(
|
|
||||||
OpBuilder &builder, OperationState &result, ValueRange inputs,
|
|
||||||
ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
|
|
||||||
ArrayRef<StringRef> iteratorTypes,
|
|
||||||
function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
|
|
||||||
bodyBuild) {
|
|
||||||
build(builder, result, inputs, outputs, indexingMaps, iteratorTypes,
|
|
||||||
/*doc=*/"", /*libraryCall=*/"", bodyBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
void IndexedGenericOp::build(
|
|
||||||
OpBuilder &builder, OperationState &result, TypeRange resultTensorTypes,
|
|
||||||
ValueRange inputs, ValueRange outputs, ArrayRef<AffineMap> indexingMaps,
|
|
||||||
ArrayRef<StringRef> iteratorTypes,
|
|
||||||
function_ref<void(OpBuilder &, Location, ValueRange, ValueRange)>
|
|
||||||
bodyBuild) {
|
|
||||||
build(builder, result, resultTensorTypes, inputs, outputs, indexingMaps,
|
|
||||||
iteratorTypes,
|
|
||||||
/*doc=*/"",
|
|
||||||
/*libraryCall=*/"", bodyBuild);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename GenericOpType>
|
|
||||||
static void printGenericOp(OpAsmPrinter &p, GenericOpType op) {
|
|
||||||
p << op.getOperationName() << " ";
|
p << op.getOperationName() << " ";
|
||||||
|
|
||||||
// Print extra attributes.
|
// Print extra attributes.
|
||||||
|
@ -628,12 +567,6 @@ static void printGenericOp(OpAsmPrinter &p, GenericOpType op) {
|
||||||
printNamedStructuredOpResults(p, op.result_tensors().getTypes());
|
printNamedStructuredOpResults(p, op.result_tensors().getTypes());
|
||||||
}
|
}
|
||||||
|
|
||||||
static void print(OpAsmPrinter &p, GenericOp op) { printGenericOp(p, op); }
|
|
||||||
|
|
||||||
static void print(OpAsmPrinter &p, IndexedGenericOp op) {
|
|
||||||
printGenericOp(p, op);
|
|
||||||
}
|
|
||||||
|
|
||||||
static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
|
static ParseResult parseGenericOp(OpAsmParser &parser, OperationState &result) {
|
||||||
DictionaryAttr dictAttr;
|
DictionaryAttr dictAttr;
|
||||||
// Parse the core linalg traits that must check into a dictAttr.
|
// Parse the core linalg traits that must check into a dictAttr.
|
||||||
|
@ -704,15 +637,6 @@ void GenericOp::getEffects(
|
||||||
outputBuffers);
|
outputBuffers);
|
||||||
}
|
}
|
||||||
|
|
||||||
void IndexedGenericOp::getEffects(
|
|
||||||
SmallVectorImpl<SideEffects::EffectInstance<MemoryEffects::Effect>>
|
|
||||||
&effects) {
|
|
||||||
SmallVector<Value> inputBuffers = getInputBufferOperands();
|
|
||||||
SmallVector<Value> outputBuffers = getOutputBufferOperands();
|
|
||||||
getGenericEffectsImpl(effects, getOperation()->getResults(), inputBuffers,
|
|
||||||
outputBuffers);
|
|
||||||
}
|
|
||||||
|
|
||||||
template <typename GenericOpType>
|
template <typename GenericOpType>
|
||||||
static LogicalResult verifyGenericOp(GenericOpType op) {
|
static LogicalResult verifyGenericOp(GenericOpType op) {
|
||||||
return success();
|
return success();
|
||||||
|
@ -720,52 +644,6 @@ static LogicalResult verifyGenericOp(GenericOpType op) {
|
||||||
|
|
||||||
static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
|
static LogicalResult verify(GenericOp op) { return verifyGenericOp(op); }
|
||||||
|
|
||||||
static LogicalResult verify(IndexedGenericOp op) { return verifyGenericOp(op); }
|
|
||||||
|
|
||||||
namespace {
|
|
||||||
|
|
||||||
/// Replace indexed_generic ops by generic ops that access the iteration indices
|
|
||||||
/// using index operation calls.
|
|
||||||
struct ConvertIndexedToGenericOp : OpRewritePattern<IndexedGenericOp> {
|
|
||||||
using OpRewritePattern<IndexedGenericOp>::OpRewritePattern;
|
|
||||||
LogicalResult matchAndRewrite(IndexedGenericOp indexedOp,
|
|
||||||
PatternRewriter &rewriter) const override {
|
|
||||||
// Replace all uses of the index block arguments.
|
|
||||||
BlockAndValueMapping bvm;
|
|
||||||
if (Block *body = indexedOp.getBody()) {
|
|
||||||
rewriter.setInsertionPointToStart(body);
|
|
||||||
for (const auto &en : llvm::enumerate(
|
|
||||||
body->getArguments().take_front(indexedOp.getNumLoops()))) {
|
|
||||||
Value index = rewriter.create<IndexOp>(indexedOp.getLoc(), en.index());
|
|
||||||
bvm.map(en.value(), index);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create a generic replacement operation and clone the body.
|
|
||||||
rewriter.setInsertionPointAfter(indexedOp);
|
|
||||||
SmallVector<Value> inputOperands = indexedOp.getInputOperands();
|
|
||||||
SmallVector<Value> outputOperands = indexedOp.getOutputOperands();
|
|
||||||
SmallVector<StringRef> iterators = llvm::to_vector<4>(
|
|
||||||
indexedOp.iterator_types().getAsValueRange<StringAttr>());
|
|
||||||
GenericOp genericOp = rewriter.create<GenericOp>(
|
|
||||||
indexedOp.getLoc(), indexedOp->getResultTypes(), inputOperands,
|
|
||||||
outputOperands, indexedOp.getIndexingMaps(), iterators);
|
|
||||||
Region &genericRegion = genericOp.region();
|
|
||||||
Region &indexedRegion = indexedOp.region();
|
|
||||||
rewriter.cloneRegionBefore(indexedRegion, genericRegion,
|
|
||||||
genericRegion.begin(), bvm);
|
|
||||||
|
|
||||||
rewriter.replaceOp(indexedOp, genericOp->getResults());
|
|
||||||
return success();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} // namespace
|
|
||||||
|
|
||||||
void IndexedGenericOp::getCanonicalizationPatterns(RewritePatternSet &results,
|
|
||||||
MLIRContext *context) {
|
|
||||||
results.add<ConvertIndexedToGenericOp>(context);
|
|
||||||
}
|
|
||||||
|
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
// InitTensorOp
|
// InitTensorOp
|
||||||
//===----------------------------------------------------------------------===//
|
//===----------------------------------------------------------------------===//
|
||||||
|
@ -3230,7 +3108,7 @@ struct DeduplicateInputs : public OpInterfaceRewritePattern<LinalgOp> {
|
||||||
PatternRewriter &rewriter) const override {
|
PatternRewriter &rewriter) const override {
|
||||||
// This pattern reduces the number of arguments of an op, which breaks
|
// This pattern reduces the number of arguments of an op, which breaks
|
||||||
// the invariants of semantically charged named ops.
|
// the invariants of semantically charged named ops.
|
||||||
if (!isa<GenericOp, IndexedGenericOp>(op))
|
if (!isa<GenericOp>(op))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
// Associate each input to an equivalent "canonical" input that has the same
|
// Associate each input to an equivalent "canonical" input that has the same
|
||||||
|
@ -3290,10 +3168,6 @@ struct DeduplicateInputs : public OpInterfaceRewritePattern<LinalgOp> {
|
||||||
// the value from the original op.
|
// the value from the original op.
|
||||||
newLinalgOp.setNumInputs(canonicalInput.size());
|
newLinalgOp.setNumInputs(canonicalInput.size());
|
||||||
|
|
||||||
// linalg.indexed_generic payloads have additional arguments prepended to
|
|
||||||
// the block arg list.
|
|
||||||
int bbArgBaseOffset = newLinalgOp.getNumPayloadInductionVariables();
|
|
||||||
|
|
||||||
// Repair the payload entry block by RAUW'ing redundant arguments and
|
// Repair the payload entry block by RAUW'ing redundant arguments and
|
||||||
// erasing them.
|
// erasing them.
|
||||||
Block &payload = newOp->getRegion(0).front();
|
Block &payload = newOp->getRegion(0).front();
|
||||||
|
@ -3305,10 +3179,10 @@ struct DeduplicateInputs : public OpInterfaceRewritePattern<LinalgOp> {
|
||||||
unsigned operandNumber = opOperand->getOperandNumber();
|
unsigned operandNumber = opOperand->getOperandNumber();
|
||||||
if (canonicalInputIndices[operandNumber] == operandNumber)
|
if (canonicalInputIndices[operandNumber] == operandNumber)
|
||||||
continue;
|
continue;
|
||||||
payload.getArgument(bbArgBaseOffset + operandNumber)
|
payload.getArgument(operandNumber)
|
||||||
.replaceAllUsesWith(payload.getArgument(
|
.replaceAllUsesWith(
|
||||||
bbArgBaseOffset + canonicalInputIndices[operandNumber]));
|
payload.getArgument(canonicalInputIndices[operandNumber]));
|
||||||
payload.eraseArgument(bbArgBaseOffset + operandNumber);
|
payload.eraseArgument(operandNumber);
|
||||||
}
|
}
|
||||||
|
|
||||||
rewriter.replaceOp(op, newOp->getResults());
|
rewriter.replaceOp(op, newOp->getResults());
|
||||||
|
@ -3316,7 +3190,7 @@ struct DeduplicateInputs : public OpInterfaceRewritePattern<LinalgOp> {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/// Remove generic/indexed_generic operations (on tensors) that are just copying
|
/// Remove generic operations (on tensors) that are just copying
|
||||||
/// the values from inputs to the results. Requirements are
|
/// the values from inputs to the results. Requirements are
|
||||||
/// 1) All iterator types are parallel
|
/// 1) All iterator types are parallel
|
||||||
/// 2) The body contains just a yield operation with the yielded values being
|
/// 2) The body contains just a yield operation with the yielded values being
|
||||||
|
@ -3335,7 +3209,7 @@ struct RemoveIdentityLinalgOps : public OpInterfaceRewritePattern<LinalgOp> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isa<GenericOp, IndexedGenericOp>(op))
|
if (!isa<GenericOp>(op))
|
||||||
return failure();
|
return failure();
|
||||||
if (!op.hasTensorSemantics())
|
if (!op.hasTensorSemantics())
|
||||||
return failure();
|
return failure();
|
||||||
|
|
|
@ -202,10 +202,6 @@ public:
|
||||||
LogicalResult
|
LogicalResult
|
||||||
matchAndRewrite(LinalgOp op, ArrayRef<Value> operands,
|
matchAndRewrite(LinalgOp op, ArrayRef<Value> operands,
|
||||||
ConversionPatternRewriter &rewriter) const final {
|
ConversionPatternRewriter &rewriter) const final {
|
||||||
// Canonicalize indexed generic operations before bufferization.
|
|
||||||
if (isa<IndexedGenericOp>(op))
|
|
||||||
return failure();
|
|
||||||
|
|
||||||
// GenericOpAdaptor below expects an `operand_segment_sizes` attribute.
|
// GenericOpAdaptor below expects an `operand_segment_sizes` attribute.
|
||||||
if (!op->hasAttr("operand_segment_sizes"))
|
if (!op->hasAttr("operand_segment_sizes"))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
|
@ -230,7 +230,6 @@ static LinalgOp fuse(OpBuilder &b, LinalgOp producer,
|
||||||
// When the producer has index semantics, we have to transform the indices of
|
// When the producer has index semantics, we have to transform the indices of
|
||||||
// the producer according to the tiling of the consumer, i.e. offset them by
|
// the producer according to the tiling of the consumer, i.e. offset them by
|
||||||
// the values computed in `loopRanges`.
|
// the values computed in `loopRanges`.
|
||||||
assert(!isa<IndexedGenericOp>(producer) && "unexpected op");
|
|
||||||
if (producer.hasIndexSemantics()) {
|
if (producer.hasIndexSemantics()) {
|
||||||
assert(clonedOp->getNumRegions() == 1 &&
|
assert(clonedOp->getNumRegions() == 1 &&
|
||||||
clonedOp->getRegion(0).getBlocks().size() == 1 &&
|
clonedOp->getRegion(0).getBlocks().size() == 1 &&
|
||||||
|
@ -426,10 +425,6 @@ mlir::linalg::fuseProducerOfBuffer(OpBuilder &b, OpOperand &consumerOpOperand,
|
||||||
if (!fusableDependence)
|
if (!fusableDependence)
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
|
|
||||||
// Canonicalize indexed generic ops before fusion.
|
|
||||||
if (isa<IndexedGenericOp>(fusableDependence->getDependentOp()))
|
|
||||||
return llvm::None;
|
|
||||||
|
|
||||||
LinalgOp producerOp = dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
|
LinalgOp producerOp = dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
|
||||||
if (!producerOp)
|
if (!producerOp)
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
|
@ -507,10 +502,6 @@ mlir::linalg::fuseProducerOfTensor(OpBuilder &b, OpOperand &consumerOpOperand) {
|
||||||
Optional<FusionInfo>
|
Optional<FusionInfo>
|
||||||
mlir::linalg::fuseProducerOfTensor(OpBuilder &b, OpResult producerOpResult,
|
mlir::linalg::fuseProducerOfTensor(OpBuilder &b, OpResult producerOpResult,
|
||||||
OpOperand &consumerOpOperand) {
|
OpOperand &consumerOpOperand) {
|
||||||
// Canonicalize indexed generic ops before fusion.
|
|
||||||
if (isa<IndexedGenericOp>(producerOpResult.getOwner()))
|
|
||||||
return llvm::None;
|
|
||||||
|
|
||||||
auto producerOp = dyn_cast<LinalgOp>(producerOpResult.getOwner());
|
auto producerOp = dyn_cast<LinalgOp>(producerOpResult.getOwner());
|
||||||
if (!producerOp)
|
if (!producerOp)
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
|
@ -766,9 +757,6 @@ FusableOpDependencesTy mlir::linalg::findAllFusableDependences(
|
||||||
fusableDependence = findFusableProducer(*opOperand, dependenceGraph);
|
fusableDependence = findFusableProducer(*opOperand, dependenceGraph);
|
||||||
if (!fusableDependence)
|
if (!fusableDependence)
|
||||||
continue;
|
continue;
|
||||||
// Canonicalize indexed generic ops before fusion.
|
|
||||||
if (isa<IndexedGenericOp>(fusableDependence->getDependentOp()))
|
|
||||||
continue;
|
|
||||||
LinalgOp producerOp =
|
LinalgOp producerOp =
|
||||||
dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
|
dyn_cast<LinalgOp>(fusableDependence->getDependentOp());
|
||||||
if (!producerOp)
|
if (!producerOp)
|
||||||
|
|
|
@ -1402,7 +1402,6 @@ void mlir::linalg::populateElementwiseOpsFusionPatterns(
|
||||||
options.controlFoldingReshapesFn);
|
options.controlFoldingReshapesFn);
|
||||||
AffineApplyOp::getCanonicalizationPatterns(patterns, context);
|
AffineApplyOp::getCanonicalizationPatterns(patterns, context);
|
||||||
GenericOp::getCanonicalizationPatterns(patterns, context);
|
GenericOp::getCanonicalizationPatterns(patterns, context);
|
||||||
IndexedGenericOp::getCanonicalizationPatterns(patterns, context);
|
|
||||||
TensorExpandShapeOp::getCanonicalizationPatterns(patterns, context);
|
TensorExpandShapeOp::getCanonicalizationPatterns(patterns, context);
|
||||||
TensorCollapseShapeOp::getCanonicalizationPatterns(patterns, context);
|
TensorCollapseShapeOp::getCanonicalizationPatterns(patterns, context);
|
||||||
context->getLoadedDialect<LinalgDialect>()->getCanonicalizationPatterns(
|
context->getLoadedDialect<LinalgDialect>()->getCanonicalizationPatterns(
|
||||||
|
|
|
@ -130,8 +130,8 @@ struct LinalgNamedOpGeneralizationPattern : RewritePattern {
|
||||||
if (failed(marker.checkAndNotify(rewriter, linalgOp)))
|
if (failed(marker.checkAndNotify(rewriter, linalgOp)))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
// No nothing to do for linalg.generic and linalg.indexed_generic.
|
// No nothing to do for linalg.generic.
|
||||||
if (isa<GenericOp, IndexedGenericOp>(rootOp))
|
if (isa<GenericOp>(rootOp))
|
||||||
return failure();
|
return failure();
|
||||||
|
|
||||||
GenericOp genericOp = createGenericOpFromNamedOp(linalgOp, rewriter);
|
GenericOp genericOp = createGenericOpFromNamedOp(linalgOp, rewriter);
|
||||||
|
|
|
@ -418,10 +418,6 @@ static Optional<LinalgLoops> linalgOpToLoopsImpl(PatternRewriter &rewriter,
|
||||||
typename std::conditional<std::is_same<LoopTy, AffineForOp>::value,
|
typename std::conditional<std::is_same<LoopTy, AffineForOp>::value,
|
||||||
AffineStoreOp, memref::StoreOp>::type;
|
AffineStoreOp, memref::StoreOp>::type;
|
||||||
|
|
||||||
// Canonicalize indexed_generic operations before lowering them to loops.
|
|
||||||
if (isa<IndexedGenericOp>(linalgOp))
|
|
||||||
return llvm::None;
|
|
||||||
|
|
||||||
// The flattened loopToOperandRangesMaps is expected to be an invertible
|
// The flattened loopToOperandRangesMaps is expected to be an invertible
|
||||||
// permutation map (which is asserted in the inverse calculation).
|
// permutation map (which is asserted in the inverse calculation).
|
||||||
assert(linalgOp.hasBufferSemantics() &&
|
assert(linalgOp.hasBufferSemantics() &&
|
||||||
|
|
|
@ -163,10 +163,6 @@ tileLinalgOpImpl(OpBuilder &b, LinalgOp op, ValueRange tileSizes,
|
||||||
if (llvm::all_of(tileSizes, isZero))
|
if (llvm::all_of(tileSizes, isZero))
|
||||||
return llvm::None;
|
return llvm::None;
|
||||||
|
|
||||||
// Canonicalize indexed generic operations before tiling.
|
|
||||||
if (isa<IndexedGenericOp>(op))
|
|
||||||
return llvm::None;
|
|
||||||
|
|
||||||
if (auto convOp = dyn_cast<linalg::ConvOp>(op.getOperation())) {
|
if (auto convOp = dyn_cast<linalg::ConvOp>(op.getOperation())) {
|
||||||
// For conv op only support tiling along batch dimension (which is the first
|
// For conv op only support tiling along batch dimension (which is the first
|
||||||
// loop).
|
// loop).
|
||||||
|
|
|
@ -89,26 +89,3 @@ func @multiple_different_redundant_args(%arg0: tensor<?xf32>, %arg1: tensor<?xf3
|
||||||
} -> tensor<?xf32>
|
} -> tensor<?xf32>
|
||||||
return %0 : tensor<?xf32>
|
return %0 : tensor<?xf32>
|
||||||
}
|
}
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
// Test case: linalg.indexed_generic.
|
|
||||||
// Other than the payload argument handling, everything else is the same.
|
|
||||||
|
|
||||||
#map = affine_map<(d0) -> (d0)>
|
|
||||||
|
|
||||||
// CHECK: #[[$MAP:.*]] = affine_map<(d0) -> (d0)>
|
|
||||||
// CHECK-LABEL: @indexed_generic
|
|
||||||
func @indexed_generic(%arg0: tensor<?xf32>) -> tensor<?xf32> {
|
|
||||||
// CHECK: linalg.generic
|
|
||||||
// CHECK: ^bb0(%[[BBARG:.*]]: f32, %{{[a-zA-Z0-9]+}}: f32):
|
|
||||||
// CHECK: addf %[[BBARG]], %[[BBARG]]
|
|
||||||
%0 = linalg.indexed_generic {indexing_maps = [#map, #map, #map], iterator_types = ["parallel"]}
|
|
||||||
ins(%arg0, %arg0 : tensor<?xf32>, tensor<?xf32>)
|
|
||||||
outs(%arg0 : tensor<?xf32>) {
|
|
||||||
^bb0(%index: index, %arg1: f32, %arg2: f32, %arg3: f32):
|
|
||||||
%1 = addf %arg1, %arg2 : f32
|
|
||||||
linalg.yield %1 : f32
|
|
||||||
} -> tensor<?xf32>
|
|
||||||
return %0 : tensor<?xf32>
|
|
||||||
}
|
|
||||||
|
|
|
@ -842,39 +842,6 @@ func @fold_tiled_loop_inputs(%A: memref<192xf32>, %A_tensor: tensor<192xf32>,
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
#map = affine_map<(d0, d1) -> (d0, d1)>
|
|
||||||
|
|
||||||
func @indexed_generic(%arg0: memref<?x?xindex>, %arg1: memref<?x?xindex>) {
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [#map, #map],
|
|
||||||
iterator_types = ["parallel", "parallel"]}
|
|
||||||
ins(%arg0 : memref<?x?xindex>)
|
|
||||||
outs(%arg1 : memref<?x?xindex>) {
|
|
||||||
^bb0(%arg4: index, %arg5: index, %arg6: index, %arg7: index):
|
|
||||||
%0 = addi %arg4, %arg5 : index
|
|
||||||
%1 = addi %0, %arg6 : index
|
|
||||||
%2 = addi %1, %arg7 : index
|
|
||||||
linalg.yield %2 : index
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// CHECK: #[[MAP:.+]] = affine_map<(d0, d1) -> (d0, d1)>
|
|
||||||
// CHECK: func @indexed_generic
|
|
||||||
// CHECK-NEXT: linalg.generic {
|
|
||||||
// CHECK-SAME: indexing_maps = [#[[MAP]], #[[MAP]]], iterator_types = ["parallel", "parallel"]}
|
|
||||||
// CHECK-SAME: ins(%[[ARG0:[A-Za-z0-9_]+]] : memref<?x?xindex>)
|
|
||||||
// CHECK-SAME: outs(%[[ARG1:[A-Za-z0-9_]+]] : memref<?x?xindex>)
|
|
||||||
// CHECK: ^bb0(%[[ARG2:[A-Za-z0-9_]+]]: index, %[[ARG3:[A-Za-z0-9_]+]]: index):
|
|
||||||
// CHECK-NEXT: %[[IDX0:.+]] = linalg.index 0 : index
|
|
||||||
// CHECK-NEXT: %[[IDX1:.+]] = linalg.index 1 : index
|
|
||||||
// CHECK-NEXT: %[[SUM0:.+]] = addi %[[IDX0]], %[[IDX1]] : index
|
|
||||||
// CHECK-NEXT: %[[SUM1:.+]] = addi %[[SUM0]], %[[ARG2]] : index
|
|
||||||
// CHECK-NEXT: %[[SUM2:.+]] = addi %[[SUM1]], %[[ARG3]] : index
|
|
||||||
// CHECK-NEXT: linalg.yield %[[SUM2]] : index
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @tensor_pad_cast_fold(%arg0: tensor<4x4xf32>) -> tensor<4x4xf32> {
|
func @tensor_pad_cast_fold(%arg0: tensor<4x4xf32>) -> tensor<4x4xf32> {
|
||||||
%c0 = constant 0 : index
|
%c0 = constant 0 : index
|
||||||
%cst = constant 0.0 : f32
|
%cst = constant 0.0 : f32
|
||||||
|
|
|
@ -227,72 +227,6 @@ func @generic_scalar_operand_block_arg_type(%arg0: f32) {
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
func @indexed_generic_block_arg_count(%arg0: memref<?xf32>) {
|
|
||||||
// expected-error @+1 {{expected as many non-induction variable region arguments as the number of input/output operands}}
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [ affine_map<(i) -> (i)> ],
|
|
||||||
iterator_types = ["parallel"]}
|
|
||||||
outs(%arg0 : memref<?xf32>) {
|
|
||||||
^bb(%f: f32):
|
|
||||||
linalg.yield %f : f32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @indexed_generic_block_induction_var_arg_type(%arg0: memref<?xf32>) {
|
|
||||||
// expected-error @+1 {{op expected index block argument #0}}
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [ affine_map<(d0) -> (d0)> ],
|
|
||||||
iterator_types = ["parallel"]}
|
|
||||||
outs(%arg0 : memref<?xf32>) {
|
|
||||||
^bb(%i: f64, %f: f32):
|
|
||||||
linalg.yield %f: f32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @indexed_generic_block_arg_type(%arg0: memref<?xf32>) {
|
|
||||||
// expected-error @+1 {{expected type of bb argument #1 ('i1') to match element or self type of the corresponding operand ('f32')}}
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [ affine_map<(d0) -> (d0)> ],
|
|
||||||
iterator_types = ["parallel"]}
|
|
||||||
outs(%arg0 : memref<?xf32>) {
|
|
||||||
^bb(%i: index, %f: i1):
|
|
||||||
linalg.yield %i: index
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @indexed_generic_arg_count(%arg0: memref<f32>) {
|
|
||||||
// expected-error @+1 {{expected as many non-induction variable region arguments as the number of input/output operands}}
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [ affine_map<()[] -> ()> ],
|
|
||||||
iterator_types = []}
|
|
||||||
outs(%arg0 : memref<f32>) {
|
|
||||||
^bb(%0: index, %1: f32):
|
|
||||||
linalg.yield %1: f32
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @indexed_generic_result_count(%arg0: memref<?xf32>) {
|
|
||||||
// expected-error @+6 {{op expected number of yield values (1) to match the number of operands of the enclosing LinalgOp (2)}}
|
|
||||||
linalg.indexed_generic {
|
|
||||||
indexing_maps = [ affine_map<(d0) -> (d0)> ],
|
|
||||||
iterator_types = ["parallel"]}
|
|
||||||
outs(%arg0 : memref<?xf32>) {
|
|
||||||
^bb(%i: index, %val: f32):
|
|
||||||
linalg.yield %val, %val: f32, f32
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
func @generic_result_0_element_type(%arg0: memref<?xf32, affine_map<(i)[off]->(off + i)>>) {
|
func @generic_result_0_element_type(%arg0: memref<?xf32, affine_map<(i)[off]->(off + i)>>) {
|
||||||
// expected-error @+7 {{type of yield operand 1 ('i1') doesn't match the element type of the enclosing linalg.generic op ('f32')}}
|
// expected-error @+7 {{type of yield operand 1 ('i1') doesn't match the element type of the enclosing linalg.generic op ('f32')}}
|
||||||
linalg.generic {
|
linalg.generic {
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
// RUN: mlir-opt %s | mlir-opt | FileCheck %s
|
// RUN: mlir-opt %s | mlir-opt | FileCheck %s
|
||||||
// RUN: mlir-opt %s --mlir-print-op-generic | mlir-opt | FileCheck %s
|
// RUN: mlir-opt %s --mlir-print-op-generic | mlir-opt | FileCheck %s
|
||||||
|
|
||||||
// TODO: Re-enable LLVM lowering test after IndexedGenericOp is lowered.
|
// TODO: Re-enable LLVM lowering test.
|
||||||
//
|
//
|
||||||
// Test that we can lower all the way to LLVM without crashing, don't check results here.
|
// Test that we can lower all the way to LLVM without crashing, don't check results here.
|
||||||
// DISABLED: mlir-opt %s --convert-linalg-to-llvm -o=/dev/null 2>&1
|
// DISABLED: mlir-opt %s --convert-linalg-to-llvm -o=/dev/null 2>&1
|
||||||
|
@ -457,43 +457,6 @@ func @generic_with_multiple_tensor_outputs(
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
#accesses_2 = [
|
|
||||||
affine_map<(i, j, k) -> (j, i)>,
|
|
||||||
affine_map<(i, j, k) -> (i, k, i + j)>,
|
|
||||||
affine_map<(i, j, k) -> (i, k, i + j)>
|
|
||||||
]
|
|
||||||
|
|
||||||
#trait_2 = {
|
|
||||||
indexing_maps = #accesses_2,
|
|
||||||
iterator_types = ["parallel", "parallel", "parallel"],
|
|
||||||
library_call = "some_external_function_name_1"
|
|
||||||
}
|
|
||||||
|
|
||||||
func @indexed_generic_with_tensor_input_and_output(
|
|
||||||
%arg0: tensor<?x?xvector<3x4xi4>>, %arg1: tensor<?x?x?xf32>)
|
|
||||||
-> (tensor<?x?x?xf32>) {
|
|
||||||
%0 = linalg.indexed_generic #trait_2
|
|
||||||
ins(%arg0, %arg1 : tensor<?x?xvector<3x4xi4>>, tensor<?x?x?xf32>)
|
|
||||||
outs(%arg1 : tensor<?x?x?xf32>)
|
|
||||||
attrs = {foo = 1} {
|
|
||||||
^bb(%i: index, %j: index, %k: index, %0: vector<3x4xi4>, %1: f32, %2: f32) :
|
|
||||||
%f0 = constant 0.0 : f32
|
|
||||||
linalg.yield %f0 : f32
|
|
||||||
} -> tensor<?x?x?xf32>
|
|
||||||
return %0 : tensor<?x?x?xf32>
|
|
||||||
}
|
|
||||||
// CHECK-LABEL: func @indexed_generic_with_tensor_input_and_output
|
|
||||||
// CHECK: linalg.indexed_generic {
|
|
||||||
// CHECK-SAME: indexing_maps = [#{{.*}}, #{{.*}}], iterator_types = ["parallel", "parallel", "parallel"],
|
|
||||||
// CHECK-SAME: library_call = "some_external_function_name_1"}
|
|
||||||
// CHECK-SAME: ins({{.*}} : tensor<?x?xvector<3x4xi4>>, tensor<?x?x?xf32>)
|
|
||||||
// CHECK-SAME: outs({{.*}} : tensor<?x?x?xf32>)
|
|
||||||
// CHECK-SAME: {foo = 1 : i64}
|
|
||||||
// CHECK: -> tensor<?x?x?xf32>
|
|
||||||
// CHECK: return {{.*}} : tensor<?x?x?xf32>
|
|
||||||
|
|
||||||
// -----
|
|
||||||
|
|
||||||
#broadcast_access = [
|
#broadcast_access = [
|
||||||
affine_map<(i, j) -> ()>,
|
affine_map<(i, j) -> ()>,
|
||||||
affine_map<(i, j) -> (i, j)>
|
affine_map<(i, j) -> (i, j)>
|
||||||
|
@ -516,17 +479,6 @@ func @generic_op_zero_rank(%arg0: tensor<f32>, %arg1 : tensor<3x4xf32>) -> (tens
|
||||||
return %0 : tensor<3x4xf32>
|
return %0 : tensor<3x4xf32>
|
||||||
}
|
}
|
||||||
|
|
||||||
func @indexed_generic_op_zero_rank(%arg0: tensor<f32>, %arg1 : tensor<3x4xf32>) -> (tensor<3x4xf32>)
|
|
||||||
{
|
|
||||||
%0 = linalg.indexed_generic #trait_broadcast
|
|
||||||
ins(%arg0 : tensor<f32>)
|
|
||||||
outs(%arg1 : tensor<3x4xf32>) {
|
|
||||||
^bb(%i: index, %j: index, %a: f32, %b: f32) :
|
|
||||||
linalg.yield %a : f32
|
|
||||||
} -> tensor<3x4xf32>
|
|
||||||
return %0 : tensor<3x4xf32>
|
|
||||||
}
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
|
|
||||||
|
@ -569,29 +521,6 @@ func @generic_region(%arg0: memref<?x?xvector<3x4xi4>, offset: ?, strides: [?, 1
|
||||||
// CHECK: %{{.*}} = linalg.index 2 : index
|
// CHECK: %{{.*}} = linalg.index 2 : index
|
||||||
// CHECK: linalg.yield %{{.*}} : f32
|
// CHECK: linalg.yield %{{.*}} : f32
|
||||||
|
|
||||||
func @indexed_generic(%arg0: memref<?x?xvector<3x4xi4>, offset: ?, strides: [?, 1]>,
|
|
||||||
%arg1: memref<?x?x?xf32, offset: ?, strides: [?, ?, 1]>) {
|
|
||||||
linalg.indexed_generic #trait_3
|
|
||||||
ins(%arg0 : memref<?x?xvector<3x4xi4>, offset: ?, strides: [?, 1]>)
|
|
||||||
outs(%arg1 : memref<?x?x?xf32, offset: ?, strides: [?, ?, 1]>)
|
|
||||||
attrs = {foo = 1} {
|
|
||||||
^bb(%i: index, %j: index, %k: index, %a: vector<3x4xi4>, %b: f32) :
|
|
||||||
linalg.yield %b : f32
|
|
||||||
}
|
|
||||||
return
|
|
||||||
}
|
|
||||||
// CHECK-LABEL: func @indexed_generic
|
|
||||||
// CHECK: linalg.indexed_generic {
|
|
||||||
// CHECK-SAME: indexing_maps = [#{{[0-9a-z]*}}, #{{[0-9a-z]*}}],
|
|
||||||
// CHECK-SAME: iterator_types = ["parallel", "parallel", "parallel"],
|
|
||||||
// CHECK-SAME: library_call = "some_external_function_name_2"
|
|
||||||
// CHECK-SAME: ins({{.*}} : memref<?x?xvector<3x4xi4>, #[[$strided2D]]>)
|
|
||||||
// CHECK-SAME: outs({{.*}} : memref<?x?x?xf32, #[[$strided3D]]>)
|
|
||||||
// CHECK-SAME: {foo = 1 : i64}
|
|
||||||
// CHECK: ^{{.*}}(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index, %{{.*}}: vector<3x4xi4>, %{{.*}}: f32):
|
|
||||||
// CHECK: linalg.yield %{{.*}} : f32
|
|
||||||
// CHECK: }
|
|
||||||
|
|
||||||
// -----
|
// -----
|
||||||
|
|
||||||
func @reshape_static(%arg0: memref<3x4x5xf32>, %arg1: tensor<3x4x5xf32>,
|
func @reshape_static(%arg0: memref<3x4x5xf32>, %arg1: tensor<3x4x5xf32>,
|
||||||
|
|
Loading…
Reference in New Issue