Skip to content

Commit

Permalink
[mlir] Move memref.[tensor_load|buffer_cast|clone] to "bufferization"…
Browse files Browse the repository at this point in the history
  • Loading branch information
pifon2a committed Nov 25, 2021
1 parent 43dc6d5 commit 57470ab
Show file tree
Hide file tree
Showing 106 changed files with 1,580 additions and 1,250 deletions.
36 changes: 18 additions & 18 deletions mlir/docs/BufferDeallocationInternals.md
Expand Up @@ -236,12 +236,12 @@ func @branch(%arg0: i1) {
cond_br %arg0, ^bb1, ^bb2
^bb1:
%1 = memref.alloc() : memref<2xf32>
%3 = memref.clone %1 : (memref<2xf32>) -> (memref<2xf32>)
%3 = bufferization.clone %1 : (memref<2xf32>) -> (memref<2xf32>)
memref.dealloc %1 : memref<2xf32> // %1 can be safely freed here
br ^bb3(%3 : memref<2xf32>)
^bb2:
use(%0)
%4 = memref.clone %0 : (memref<2xf32>) -> (memref<2xf32>)
%4 = bufferization.clone %0 : (memref<2xf32>) -> (memref<2xf32>)
br ^bb3(%4 : memref<2xf32>)
^bb3(%2: memref<2xf32>):
Expand Down Expand Up @@ -309,7 +309,7 @@ func @condBranchDynamicTypeNested(
cond_br %arg0, ^bb1, ^bb2(%arg3 : index)
^bb1:
// temp buffer required due to alias %3
%5 = memref.clone %arg1 : (memref<?xf32>) -> (memref<?xf32>)
%5 = bufferization.clone %arg1 : (memref<?xf32>) -> (memref<?xf32>)
br ^bb6(%5 : memref<?xf32>)
^bb2(%0: index):
%1 = memref.alloc(%0) : memref<?xf32>
Expand All @@ -320,7 +320,7 @@ func @condBranchDynamicTypeNested(
^bb4:
br ^bb5(%1 : memref<?xf32>)
^bb5(%2: memref<?xf32>):
%6 = memref.clone %1 : (memref<?xf32>) -> (memref<?xf32>)
%6 = bufferization.clone %1 : (memref<?xf32>) -> (memref<?xf32>)
memref.dealloc %1 : memref<?xf32>
br ^bb6(%6 : memref<?xf32>)
^bb6(%3: memref<?xf32>):
Expand Down Expand Up @@ -477,15 +477,15 @@ func @inner_region_control_flow_div(
%0 = memref.alloc(%arg0, %arg0) : memref<?x?xf32>
%1 = custom.region_if %0 : memref<?x?xf32> -> (memref<?x?xf32>)
then(%arg2 : memref<?x?xf32>) {
%4 = memref.clone %arg2 : (memref<?x?xf32>) -> (memref<?x?xf32>)
%4 = bufferization.clone %arg2 : (memref<?x?xf32>) -> (memref<?x?xf32>)
custom.region_if_yield %4 : memref<?x?xf32>
} else(%arg3 : memref<?x?xf32>) {
%2 = memref.alloc(%arg0, %arg1) : memref<?x?xf32>
%5 = memref.clone %2 : (memref<?x?xf32>) -> (memref<?x?xf32>)
%5 = bufferization.clone %2 : (memref<?x?xf32>) -> (memref<?x?xf32>)
memref.dealloc %2 : memref<?x?xf32>
custom.region_if_yield %5 : memref<?x?xf32>
} join(%arg4: memref<?x?xf32>) {
%4 = memref.clone %arg4 : (memref<?x?xf32>) -> (memref<?x?xf32>)
%4 = bufferization.clone %arg4 : (memref<?x?xf32>) -> (memref<?x?xf32>)
memref.dealloc %arg4 : memref<?x?xf32>
custom.region_if_yield %4 : memref<?x?xf32>
}
Expand Down Expand Up @@ -553,21 +553,21 @@ func @loop_nested_if(
%step: index,
%buf: memref<2xf32>,
%res: memref<2xf32>) {
%4 = memref.clone %buf : (memref<2xf32>) -> (memref<2xf32>)
%4 = bufferization.clone %buf : (memref<2xf32>) -> (memref<2xf32>)
%0 = scf.for %i = %lb to %ub step %step
iter_args(%iterBuf = %4) -> memref<2xf32> {
%1 = arith.cmpi "eq", %i, %ub : index
%2 = scf.if %1 -> (memref<2xf32>) {
%3 = memref.alloc() : memref<2xf32> // makes %2 a critical alias
use(%3)
%5 = memref.clone %3 : (memref<2xf32>) -> (memref<2xf32>)
%5 = bufferization.clone %3 : (memref<2xf32>) -> (memref<2xf32>)
memref.dealloc %3 : memref<2xf32>
scf.yield %5 : memref<2xf32>
} else {
%6 = memref.clone %iterBuf : (memref<2xf32>) -> (memref<2xf32>)
%6 = bufferization.clone %iterBuf : (memref<2xf32>) -> (memref<2xf32>)
scf.yield %6 : memref<2xf32>
}
%7 = memref.clone %2 : (memref<2xf32>) -> (memref<2xf32>)
%7 = bufferization.clone %2 : (memref<2xf32>) -> (memref<2xf32>)
memref.dealloc %2 : memref<2xf32>
memref.dealloc %iterBuf : memref<2xf32> // free backedge iteration variable
scf.yield %7 : memref<2xf32>
Expand Down Expand Up @@ -626,7 +626,7 @@ and can be removed.
```mlir
func @dynamic_allocation(%arg0: index, %arg1: index) -> memref<?x?xf32> {
%1 = memref.alloc(%arg0, %arg1) : memref<?x?xf32>
%2 = memref.clone %1 : (memref<?x?xf32>) -> (memref<?x?xf32>)
%2 = bufferization.clone %1 : (memref<?x?xf32>) -> (memref<?x?xf32>)
memref.dealloc %1 : memref<?x?xf32>
return %2 : memref<?x?xf32>
}
Expand Down Expand Up @@ -667,7 +667,7 @@ func @reuseTarget(%arg0: memref<2xf32>, %result: memref<2xf32>){
%tmp2 = math.exp %gen2_arg0 : f32
test.yield %tmp2 : f32
}: memref<2xf32>, memref<2xf32>
%result = memref.clone %temp : (memref<2xf32>) -> (memref<2xf32>)
%result = bufferization.clone %temp : (memref<2xf32>) -> (memref<2xf32>)
memref.dealloc %temp : memref<2xf32>
return
}
Expand All @@ -693,8 +693,8 @@ func @reuseTarget(%arg0: memref<2xf32>, %result: memref<2xf32>){
## Known Limitations

BufferDeallocation introduces additional clones from “memref” dialect
(“memref.clone”). Analogous, all deallocations use the “memref” dialect-free
operation “memref.dealloc”. The actual copy process is realized using
“test.copy”. Furthermore, buffers are essentially immutable after their creation
in a block. Another limitations are known in the case using unstructered control
flow.
(“bufferization.clone”). Analogous, all deallocations use the “memref”
dialect-free operation “memref.dealloc”. The actual copy process is realized
using “test.copy”. Furthermore, buffers are essentially immutable after their
creation in a block. Another limitations are known in the case using
unstructered control flow.
12 changes: 6 additions & 6 deletions mlir/docs/Bufferization.md
Expand Up @@ -191,8 +191,8 @@ One convenient utility provided by the MLIR bufferization infrastructure is the
`BufferizeTypeConverter`, which comes pre-loaded with the necessary conversions
and materializations between `tensor` and `memref`.

In this case, the `MemRefOpsDialect` is marked as legal, so the
`memref.tensor_load` and `memref.buffer_cast` ops, which are inserted
In this case, the `BufferizationOpsDialect` is marked as legal, so the
`bufferization.to_tensor` and `bufferization.to_memref` ops, which are inserted
automatically by the dialect conversion framework as materializations, are
legal. There is a helper `populateBufferizeMaterializationLegality`
([code](https://github.com/llvm/llvm-project/blob/a0b65a7bcd6065688189b3d678c42ed6af9603db/mlir/include/mlir/Transforms/Bufferize.h#L53))
Expand Down Expand Up @@ -252,9 +252,9 @@ from the program.

The easiest way to write a finalizing bufferize pass is to not write one at all!
MLIR provides a pass `finalizing-bufferize` which eliminates the
`memref.tensor_load` / `memref.buffer_cast` materialization ops inserted by
partial bufferization passes and emits an error if that is not sufficient to
remove all tensors from the program.
`bufferization.to_tensor` / `bufferization.to_memref` materialization ops
inserted by partial bufferization passes and emits an error if that is not
sufficient to remove all tensors from the program.

This pass is sufficient when partial bufferization passes have bufferized all
the ops in the program, leaving behind only the materializations. When possible,
Expand All @@ -272,7 +272,7 @@ downstream projects structured this way. This structure is not recommended in
new code. A helper, `populateEliminateBufferizeMaterializationsPatterns`
([code](https://github.com/llvm/llvm-project/blob/a0b65a7bcd6065688189b3d678c42ed6af9603db/mlir/include/mlir/Transforms/Bufferize.h#L58))
is available for such passes to provide patterns that eliminate
`memref.tensor_load` and `memref.buffer_cast`.
`bufferization.to_tensor` and `bufferization.to_memref`.

## Changes since [the talk](#the-talk)

Expand Down
3 changes: 2 additions & 1 deletion mlir/include/mlir/Dialect/Arithmetic/Transforms/Passes.td
Expand Up @@ -14,7 +14,8 @@ include "mlir/Pass/PassBase.td"
def ArithmeticBufferize : FunctionPass<"arith-bufferize"> {
let summary = "Bufferize Arithmetic dialect ops.";
let constructor = "mlir::arith::createArithmeticBufferizePass()";
let dependentDialects = ["memref::MemRefDialect"];
let dependentDialects = ["bufferization::BufferizationDialect",
"memref::MemRefDialect"];
}

def ArithmeticExpandOps : FunctionPass<"arith-expand"> {
Expand Down
Expand Up @@ -13,7 +13,6 @@
#ifndef MLIR_DIALECT_BUFFERIZATION_IR_ALLOCATIONOPINTERFACE_H_
#define MLIR_DIALECT_BUFFERIZATION_IR_ALLOCATIONOPINTERFACE_H_

#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/IR/Builders.h"

#include "mlir/Dialect/Bufferization/IR/AllocationOpInterface.h.inc"
Expand Down
Expand Up @@ -50,10 +50,7 @@ def AllocationOpInterface : OpInterface<"AllocationOpInterface"> {
}],
"::mlir::Optional<::mlir::Value>", "buildClone",
(ins "::mlir::OpBuilder&":$builder, "::mlir::Value":$alloc), [{}],
/*defaultImplementation=*/[{
return builder.create<memref::CloneOp>(alloc.getLoc(), alloc)
.getResult();
}]
/*defaultImplementation=*/[{ return llvm::None; }]
>
];
}
Expand Down
29 changes: 29 additions & 0 deletions mlir/include/mlir/Dialect/Bufferization/IR/Bufferization.h
@@ -0,0 +1,29 @@
//===- Bufferization.h - Bufferization dialect ------------------*- C++ -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZATION_H_
#define MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZATION_H_

#include "mlir/Dialect/Bufferization/IR/AllocationOpInterface.h"
#include "mlir/Dialect/MemRef/IR/MemRef.h"
#include "mlir/Dialect/Tensor/IR/Tensor.h"

//===----------------------------------------------------------------------===//
// Bufferization Dialect
//===----------------------------------------------------------------------===//

#include "mlir/Dialect/Bufferization/IR/BufferizationOpsDialect.h.inc"

//===----------------------------------------------------------------------===//
// Bufferization Dialect Operations
//===----------------------------------------------------------------------===//

#define GET_OP_CLASSES
#include "mlir/Dialect/Bufferization/IR/BufferizationOps.h.inc"

#endif // MLIR_DIALECT_BUFFERIZATION_IR_BUFFERIZATION_H_
31 changes: 31 additions & 0 deletions mlir/include/mlir/Dialect/Bufferization/IR/BufferizationBase.td
@@ -0,0 +1,31 @@
//===- BufferizationBase.td - Bufferization dialect base ---*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef BUFFERIZATION_BASE
#define BUFFERIZATION_BASE

include "mlir/IR/OpBase.td"

def Bufferization_Dialect : Dialect {
let name = "bufferization";
let cppNamespace = "::mlir::bufferization";
let description = [{
Bufferization in MLIR is the process of converting the `tensor` type to the
`memref` type.
The `bufferization` dialect is intended to collect operations/interfaces
specific to the bufferization passes.

Overview of the bufferization infrastructure and important conceptual
details related to using the MLIR dialect conversion infrastructure can be
found in [bufferization](Bufferization.md) and [buffer
deallocation](BufferDeallocationInternals.md).
}];
let dependentDialects = ["memref::MemRefDialect", "tensor::TensorDialect"];
}

#endif // BUFFERIZATION_BASE
159 changes: 159 additions & 0 deletions mlir/include/mlir/Dialect/Bufferization/IR/BufferizationOps.td
@@ -0,0 +1,159 @@
//===- BufferizationOps.td - Bufferization op definitions ----------*- tablegen -*-===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//

#ifndef BUFFERIZATION_OPS
#define BUFFERIZATION_OPS

include "mlir/Dialect/Bufferization/IR/AllocationOpInterface.td"
include "mlir/Dialect/Bufferization/IR/BufferizationBase.td"
include "mlir/Interfaces/SideEffectInterfaces.td"
include "mlir/Interfaces/CopyOpInterface.td"

class Bufferization_Op<string mnemonic, list<OpTrait> traits = []>
: Op<Bufferization_Dialect, mnemonic, traits>;

//===----------------------------------------------------------------------===//
// CloneOp
//===----------------------------------------------------------------------===//

def Bufferization_CloneOp : Bufferization_Op<"clone", [
CopyOpInterface,
DeclareOpInterfaceMethods<MemoryEffectsOpInterface>,
DeclareOpInterfaceMethods<AllocationOpInterface, ["buildDealloc", "buildClone"]>
]> {
let builders = [
OpBuilder<(ins "Value":$value), [{
return build($_builder, $_state, value.getType(), value);
}]>];

let description = [{
Clones the data in the input view into an implicitly defined output view.

Usage:

```mlir
%arg1 = bufferization.clone %arg0 : memref<?xf32> to memref<?xf32>
```

Valid implementations of this operation may alias the input and output
views or create an actual copy. Mutating the source or result
of the clone operation after the clone operation thus leads to undefined
behavior.
}];

let arguments = (ins Arg<AnyRankedOrUnrankedMemRef, "", []>:$input);
let results = (outs Arg<AnyRankedOrUnrankedMemRef, "", []>:$output);

let extraClassDeclaration = [{
Value getSource() { return input(); }
Value getTarget() { return output(); }
}];

let assemblyFormat = "$input attr-dict `:` type($input) `to` type($output)";

let hasFolder = 1;
let verifier = ?;
let hasCanonicalizer = 1;
}
//===----------------------------------------------------------------------===//
// ToTensorOp
//===----------------------------------------------------------------------===//

def Bufferization_ToTensorOp : Bufferization_Op<"to_tensor",
[SameOperandsAndResultShape, SameOperandsAndResultElementType,
TypesMatchWith<"result type matches tensor equivalent of 'memref'",
"memref", "result",
"memref::getTensorTypeFromMemRefType($_self)">]> {
let summary = "memref to tensor operation";
let description = [{
Create a tensor from a memref, making an independent copy of the element
data. The result value is a tensor whose shape and element type match the
memref operand.

The opposite of this op is to_memref. Together, these two ops are
useful for source/target materializations when doing type conversions
involving tensors and memrefs.

Example:

```mlir
// Produces a value of tensor<4x?xf32> type.
%12 = bufferization.to_tensor %10 : memref<4x?xf32, #layout, memspace0>
```

If tensor load is used in the bufferization steps, mutating the source
buffer after loading leads to undefined behavior.
}];

let arguments = (ins Arg<AnyRankedOrUnrankedMemRef,
"the reference to load from", [MemRead]>:$memref);
let results = (outs AnyTensor:$result);
// MemrefToTensor is fully verified by traits.
let verifier = ?;

let builders = [
OpBuilder<(ins "Value":$memref), [{
$_state.addOperands(memref);
$_state.addTypes(memref::getTensorTypeFromMemRefType(memref.getType()));
}]>];

let extraClassDeclaration = [{
/// The result of a to_tensor is always a tensor.
TensorType getType() {
Type resultType = getResult().getType();
if (resultType.isa<TensorType>())
return resultType.cast<TensorType>();
return {};
}
}];

let assemblyFormat = "$memref attr-dict `:` type($memref)";

let hasCanonicalizer = 1;
let hasFolder = 1;
}


//===----------------------------------------------------------------------===//
// ToMemrefOp
//===----------------------------------------------------------------------===//

def Bufferization_ToMemrefOp : Bufferization_Op<"to_memref",
[SameOperandsAndResultShape, SameOperandsAndResultElementType, NoSideEffect,
TypesMatchWith<"type of 'tensor' is the tensor equivalent of 'memref'",
"memref", "tensor",
"memref::getTensorTypeFromMemRefType($_self)">]> {
let summary = "tensor to memref cast operation";
let description = [{
Casts a tensor to a memref.

```mlir
// Result type is tensor<4x?xf32>
%12 = bufferization.to_memref %10 : memref<4x?xf32, #map0, 42>
```

Note, that mutating the result of the to_buffer operation leads to
undefined behavior.

This operation is a specialized variant of the built-in
unrealized_conversion_cast and is intended for use in the context of
gradual bufferization.
}];

let arguments = (ins AnyTensor:$tensor);
let results = (outs AnyRankedOrUnrankedMemRef:$memref);
// This op is fully verified by traits.
let verifier = ?;

let assemblyFormat = "$tensor attr-dict `:` type($memref)";

let hasFolder = 1;
let hasCanonicalizer = 1;
}

#endif // BUFFERIZATION_OPS
2 changes: 2 additions & 0 deletions mlir/include/mlir/Dialect/Bufferization/IR/CMakeLists.txt
@@ -1 +1,3 @@
add_mlir_dialect(BufferizationOps bufferization)
add_mlir_doc(BufferizationOps BufferizationOps Dialects/ -gen-dialect-doc)
add_mlir_interface(AllocationOpInterface)

0 comments on commit 57470ab

Please sign in to comment.