Skip to content

Commit

Permalink
[mlir][Linalg] Introduce linalg.pooling_min/max/sum op.
Browse files Browse the repository at this point in the history
Summary:
Performs an N-D pooling operation similarly to the description in the TF
documentation:
https://www.tensorflow.org/api_docs/python/tf/nn/pool

Different from the description, this operation doesn't perform on batch and
channel. It only takes tensors of rank `N`.

```
  output[x[0], ..., x[N-1]] =
    REDUCE_{z[0], ..., z[N-1]}
      input[
            x[0] * strides[0] - pad_before[0] + dilation_rate[0]*z[0],
            ...
            x[N-1]*strides[N-1] - pad_before[N-1] + dilation_rate[N-1]*z[N-1]
            ],
```

The required optional arguments are:
  - strides: an i64 array specifying the stride (i.e. step) for window
    loops.
  - dilations: an i64 array specifying the filter upsampling/input
    downsampling rate
  - padding: an i64 array of pairs (low, high) specifying the number of
    elements to pad along a dimension.

If strides or dilations attributes are missing then the default value is
one for each of the input dimensions. Similarly, padding values are zero
for both low and high in each of the dimensions, if not specified.

Differential Revision: https://reviews.llvm.org/D76414
  • Loading branch information
hanhanW committed Apr 1, 2020
1 parent bb3111c commit 69ddee1
Show file tree
Hide file tree
Showing 9 changed files with 444 additions and 45 deletions.
16 changes: 10 additions & 6 deletions mlir/include/mlir/Dialect/Linalg/IR/LinalgOps.h
Expand Up @@ -29,6 +29,9 @@ namespace mlir {
namespace linalg {

class ConvOp;
class PoolingMaxOp;
class PoolingMinOp;
class PoolingSumOp;

/// Returns the name mangled library call name to disambiguate between different
/// overloads at the C level. The name mangling scheme is basic and uses MLIR
Expand Down Expand Up @@ -60,12 +63,13 @@ std::string generateLibraryCallName(Operation *op);
SmallVector<AffineExpr, 4> makeAffineDimExprs(unsigned num, unsigned &startIdx,
MLIRContext *context);

/// Builds the indexing expressions for a ConvOp `op`. Returns the vector of
/// AffineMaps representing:
/// `stride[i] * xs[i] + dilation[i] * zs[i] - pad_low[i]`
SmallVector<AffineExpr, 4> weightedConvInputIndex(ConvOp op,
ArrayRef<AffineExpr> xs,
ArrayRef<AffineExpr> zs);
/// Builds the indexing expressions for a ConvOp/PoolingOp `op`. Returns the
/// vector of AffineMaps representing:
/// `stride[i] * outputDims[i] + dilation[i] * windowDims[i] - pad_low[i]`
template <typename PoolingOp>
extern SmallVector<AffineExpr, 4>
weightedPoolingInputIndex(PoolingOp op, ArrayRef<AffineExpr> outputDims,
ArrayRef<AffineExpr> windowDims);

/// Returns `maybeMap.get()` if `maybeMap` is set, otherwise returns the
/// symbol-less identity map of `rank`.
Expand Down
168 changes: 145 additions & 23 deletions mlir/include/mlir/Dialect/Linalg/IR/LinalgStructuredOps.td
Expand Up @@ -251,7 +251,69 @@ def MatmulOp : LinalgStructured_Op<"matmul", [NInputs<2>, NOutputs<1>]> {
let hasFolder = 1;
}

def ConvOp : LinalgStructured_Op<"conv", [NInputs<2>, NOutputs<1>]> {
/// A base class for pooling operation such as conv. The arguments must contain
/// optional arguments `strides`, `dilations` and `padding` with following type:
/// OptionalAttr<I64ArrayAttr>:$strides
/// OptionalAttr<I64ArrayAttr>:$dilations
/// OptionalAttr<I64ElementsAttr>:$padding
/// `stirdes` denotes the step of each window along the dimension.
class PoolingBase_Op<string mnemonic, list<OpTrait> props>
: LinalgStructured_Op<mnemonic, props> {
let description = [{
Performs an N-D pooling operation similarly to the description in the TF
documentation:
https://www.tensorflow.org/api_docs/python/tf/nn/pool

Different from the description, this operation doesn't perform on batch and
channel. It only takes tensors of rank `N`.

```
output[x[0], ..., x[N-1]] =
REDUCE_{z[0], ..., z[N-1]}
input[
x[0] * strides[0] - pad_before[0] + dilation_rate[0]*z[0],
...
x[N-1]*strides[N-1] - pad_before[N-1] + dilation_rate[N-1]*z[N-1]
],
```

The required optional arguments are:
- strides: an i64 array specifying the stride (i.e. step) for window
loops.
- dilations: an i64 array specifying the filter upsampling/input
downsampling rate
- padding: an i64 array of pairs (low, high) specifying the number of
elements to pad along a dimension.

If strides or dilations attributes are missing then the default value is
one for each of the input dimensions. Similarly, padding values are zero
for both low and high in each of the dimensions, if not specified.
}];

code commonUtils = libraryCallName # [{
int64_t getStride(unsigned i) {
assert(i < getNumWindowLoops());
if (!strides().hasValue()) return 1;
return strides()->getValue()[i]
.cast<IntegerAttr>().getValue().getSExtValue();
}

int64_t getDilation(unsigned i) {
assert(i < getNumWindowLoops());
if (!dilations().hasValue()) return 1;
return dilations()->getValue()[i]
.cast<IntegerAttr>().getValue().getSExtValue();
}

int64_t getLowPad(unsigned i) {
assert(i < getNumWindowLoops());
if (!padding().hasValue()) return 0;
return padding().getValue().getValue<int64_t>({i, 0});
}
}];
}

def ConvOp : PoolingBase_Op<"conv", [NInputs<2>, NOutputs<1>]> {

let description = [{
Generic n-D convolution as described in the TF documentation:
Expand Down Expand Up @@ -282,7 +344,7 @@ def ConvOp : LinalgStructured_Op<"conv", [NInputs<2>, NOutputs<1>]> {
OptionalAttr<I64ArrayAttr>:$dilations,
OptionalAttr<I64ElementsAttr>:$padding);

let extraClassDeclaration = libraryCallName # [{
let extraClassDeclaration = commonUtils # [{
// TODO(ntv) extend to support more than 1 dimensions and potentially
// grouping too.
unsigned getNumBatchDimensions() { return 1; }
Expand All @@ -309,26 +371,6 @@ def ConvOp : LinalgStructured_Op<"conv", [NInputs<2>, NOutputs<1>]> {
return iters;
}

int64_t getStride(unsigned i) {
assert(i < getNumWindowLoops());
if (!strides().hasValue()) return 1;
return strides()->getValue()[i]
.cast<IntegerAttr>().getValue().getSExtValue();
}

int64_t getDilation(unsigned i) {
assert(i < getNumWindowLoops());
if (!dilations().hasValue()) return 1;
return dilations()->getValue()[i]
.cast<IntegerAttr>().getValue().getSExtValue();
}

int64_t getLowPad(unsigned i) {
assert(i < getNumWindowLoops());
if (!padding().hasValue()) return 0;
return padding().getValue().getValue<int64_t>({i, 0});
}

// F(z0, ..., zN-1, q, k) *
// I(b, x0 + z0 - pad_low_0, ..., xN-1 + zN-1 - pad_low_N-1, q)
// -> O(b, x0, ..., xN-1, k)
Expand Down Expand Up @@ -358,7 +400,7 @@ def ConvOp : LinalgStructured_Op<"conv", [NInputs<2>, NOutputs<1>]> {
// Window reduction dims: sum_{z[0], ..., z[N-1], q}
auto zs = makeAffineDimExprs(nWin, idx, context);
// Construct the weighedSum expression.
auto ws = weightedConvInputIndex(*this, xs, zs);
auto ws = weightedPoolingInputIndex(*this, xs, zs);
return SmallVector<AffineMap, 8>{
// filter[z[0], ..., z[N-1], q, k]
AffineMap::get(idx, 0, concat(concat(zs, qs), ks)),
Expand All @@ -378,6 +420,86 @@ def ConvOp : LinalgStructured_Op<"conv", [NInputs<2>, NOutputs<1>]> {
let hasFolder = 1;
}

class SingleInputPoolingBase_Op<string mnemonic>
: PoolingBase_Op<mnemonic, [NInputs<2>, NOutputs<1>]> {
let description = [{
A base class for single input pooling function.

TODO: Figure out a better way to handle window dimensions, i.e., eliminate
the fake memref.
The window dimensions are specified by argument `windowDims`. The i-th
dimension in the shape of `windowDims` denotes the size of the window along
dimension i. For example, if the window size is 2x3, then a memref<2x3>
should be passed to the operation as `windowDims`.
}];

let arguments = (ins AnyStridedMemRef:$input,
AnyStridedMemRef:$windowDims,
AnyStridedMemRef:$output,
OptionalAttr<I64ArrayAttr>:$strides,
OptionalAttr<I64ArrayAttr>:$dilations,
OptionalAttr<I64ElementsAttr>:$padding);

let extraClassDeclaration = commonUtils# [{
llvm::Optional<SmallVector<StringRef, 8>> referenceIterators() {
// Outer parallel loops are always the number of output dimensions.
unsigned nPar = getOutputShapedType(0).getRank();
// The window loops has the same number loops with output dimensions.
unsigned nWin = nPar;
SmallVector<StringRef, 8> iters(nPar, getParallelIteratorTypeName());
iters.reserve(nPar + nWin);
iters.append(nWin, getWindowIteratorTypeName());
return iters;
}

llvm::Optional<SmallVector<AffineMap, 8>> referenceIndexingMaps() {
MLIRContext *context = getContext();
auto nPar = getNumParallelLoops();
auto nWin = getNumWindowLoops();
assert(nWin > 0 && "expected at least one window dimension");
unsigned idx = 0;
auto outputDims = makeAffineDimExprs(nPar, idx, context);
auto windowDims = makeAffineDimExprs(nWin, idx, context);
// Construct the weighedSum expression.
auto inputDims =
weightedPoolingInputIndex(*this, outputDims, windowDims);
return SmallVector<AffineMap, 8>{
// input
AffineMap::get(idx, 0, inputDims),
// windowDims
AffineMap::get(idx, 0, windowDims),
// output
AffineMap::get(idx, 0, outputDims)
};
}
}];

let verifier = [{ return ::verify(*this); }];

let hasFolder = 1;
}

def PoolingMaxOp: SingleInputPoolingBase_Op<"pooling_max"> {
let description = [{
Takes max op as pooling operation, i.e., it samples the maximum value in the
window.
}];
}

def PoolingMinOp: SingleInputPoolingBase_Op<"pooling_min"> {
let description = [{
Takes min op as pooling operation, i.e., it samples the minimum value in the
window.
}];
}

def PoolingSumOp: SingleInputPoolingBase_Op<"pooling_sum"> {
let description = [{
Takes add op as pooling operation, i.e., it accumulates the values in the
window.
}];
}

//===----------------------------------------------------------------------===//
// Generic Linalg ops.
//===----------------------------------------------------------------------===//
Expand Down
9 changes: 9 additions & 0 deletions mlir/include/mlir/Dialect/Utils/StructuredOpsUtils.h
Expand Up @@ -72,6 +72,15 @@ constexpr StringRef getFunAttrName() { return "fun"; }
/// function that implements the structured op.
constexpr StringRef getLibraryCallAttrName() { return "library_call"; }

/// Attribute name for the StrArrayAttr which encodes the value of strides.
constexpr StringRef getStridesAttrName() { return "strides"; }

/// Attribute name for the StrArrayAttr which encodes the value of dilations.
constexpr StringRef getDilationsAttrName() { return "dilations"; }

/// Attribute name for the StrArrayAttr which encodes the value of paddings.
constexpr StringRef getPaddingAttrName() { return "padding"; }

/// Use to encode that a particular iterator type has parallel semantics.
constexpr StringRef getParallelIteratorTypeName() { return "parallel"; }

Expand Down
21 changes: 15 additions & 6 deletions mlir/lib/Conversion/LinalgToLLVM/LinalgToLLVM.cpp
Expand Up @@ -524,12 +524,21 @@ populateLinalgToStandardConversionPatterns(OwningRewritePatternList &patterns,
MLIRContext *ctx) {
// TODO(ntv) ConvOp conversion needs to export a descriptor with relevant
// attribute values such as kernel striding and dilation.
patterns.insert<CopyTransposeConversion, LinalgOpConversion<ConvOp>,
LinalgOpConversion<CopyOp>, LinalgOpConversion<DotOp>,
LinalgOpConversion<FillOp>, LinalgOpConversion<GenericOp>,
LinalgOpConversion<IndexedGenericOp>,
LinalgOpConversion<MatmulOp>, LinalgOpConversion<MatvecOp>>(
ctx);
// clang-format off
patterns.insert<
CopyTransposeConversion,
LinalgOpConversion<ConvOp>,
LinalgOpConversion<PoolingMaxOp>,
LinalgOpConversion<PoolingMinOp>,
LinalgOpConversion<PoolingSumOp>,
LinalgOpConversion<CopyOp>,
LinalgOpConversion<DotOp>,
LinalgOpConversion<FillOp>,
LinalgOpConversion<GenericOp>,
LinalgOpConversion<IndexedGenericOp>,
LinalgOpConversion<MatmulOp>,
LinalgOpConversion<MatvecOp>>(ctx);
// clang-format on
}

} // namespace
Expand Down

0 comments on commit 69ddee1

Please sign in to comment.