2020-05-07 09:41:54 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
|
|
|
// 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
|
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
//
|
2020-10-10 06:03:57 +08:00
|
|
|
// This is the base file for npcomp's "reference backend".
|
|
|
|
//
|
2021-08-03 01:27:16 +08:00
|
|
|
// The input to this backend is a layer that consists of linalg-on-tensors
|
|
|
|
// together with std scalar ops and control flow.
|
2020-10-10 06:03:57 +08:00
|
|
|
//
|
|
|
|
// The output of this backend is LLVM IR suitable for JITing.
|
|
|
|
//
|
|
|
|
// We expect that other backends will appear that have a similar kind of
|
2021-08-03 01:27:16 +08:00
|
|
|
// interface. IREE already uses this layering.
|
2020-05-07 09:41:54 +08:00
|
|
|
//
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-10-07 07:14:37 +08:00
|
|
|
#include "npcomp/RefBackend/RefBackend.h"
|
2020-05-07 09:41:54 +08:00
|
|
|
#include "PassDetail.h"
|
|
|
|
|
2020-12-16 04:53:12 +08:00
|
|
|
#include "mlir/Conversion/AffineToStandard/AffineToStandard.h"
|
2020-05-22 04:09:06 +08:00
|
|
|
#include "mlir/Conversion/SCFToStandard/SCFToStandard.h"
|
2020-09-17 08:31:40 +08:00
|
|
|
#include "mlir/Conversion/ShapeToStandard/ShapeToStandard.h"
|
2020-05-07 09:41:54 +08:00
|
|
|
#include "mlir/Dialect/Linalg/IR/LinalgOps.h"
|
|
|
|
#include "mlir/Dialect/Linalg/IR/LinalgTypes.h"
|
2020-05-12 09:50:51 +08:00
|
|
|
#include "mlir/Dialect/Linalg/Passes.h"
|
2021-03-24 05:16:23 +08:00
|
|
|
#include "mlir/Dialect/MemRef/IR/MemRef.h"
|
2020-10-27 07:53:23 +08:00
|
|
|
#include "mlir/Dialect/SCF/Passes.h"
|
2020-05-07 09:41:54 +08:00
|
|
|
#include "mlir/Dialect/StandardOps/IR/Ops.h"
|
2020-10-17 04:57:24 +08:00
|
|
|
#include "mlir/Dialect/StandardOps/Transforms/Passes.h"
|
2020-12-12 06:43:38 +08:00
|
|
|
#include "mlir/Dialect/Tensor/IR/Tensor.h"
|
|
|
|
#include "mlir/Dialect/Tensor/Transforms/Passes.h"
|
2020-05-07 09:41:54 +08:00
|
|
|
#include "mlir/Pass/Pass.h"
|
|
|
|
#include "mlir/Pass/PassRegistry.h"
|
|
|
|
#include "mlir/Transforms/DialectConversion.h"
|
2020-10-30 06:25:55 +08:00
|
|
|
#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
|
2020-05-12 06:27:37 +08:00
|
|
|
#include "mlir/Transforms/Passes.h"
|
2021-09-03 03:47:59 +08:00
|
|
|
#include "npcomp/Backend/Common/Passes.h"
|
2020-10-08 08:30:10 +08:00
|
|
|
#include "npcomp/Dialect/Refback/IR/RefbackOps.h"
|
2020-05-07 09:41:54 +08:00
|
|
|
|
|
|
|
using namespace mlir;
|
|
|
|
using namespace mlir::NPCOMP;
|
|
|
|
|
2020-08-28 06:09:10 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// Pass registration
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
#define GEN_PASS_REGISTRATION
|
2020-10-07 07:14:37 +08:00
|
|
|
#include "npcomp/RefBackend/Passes.h.inc"
|
2020-08-28 06:09:10 +08:00
|
|
|
} // end namespace
|
|
|
|
|
2020-10-07 07:14:37 +08:00
|
|
|
void mlir::NPCOMP::registerRefBackendPasses() {
|
2020-08-28 06:09:10 +08:00
|
|
|
::registerPasses();
|
|
|
|
|
2020-10-07 07:14:37 +08:00
|
|
|
mlir::PassPipelineRegistration<RefBackendLoweringPipelineOptions>(
|
|
|
|
"refback-lowering-pipeline", "RefBackend lowering pipeline.",
|
|
|
|
mlir::NPCOMP::createRefBackendLoweringPipeline);
|
2020-05-12 09:50:51 +08:00
|
|
|
}
|
|
|
|
|
2020-05-19 03:50:16 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
// LowerAllocMemRefOps
|
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
|
|
|
namespace {
|
[RefBackend] Split out RefBackend (refback) dialect from TCP.
This is the first in a patch series that is refactoring the
constellation of things variously called or associated with "E2E",
"RefE2E", "npcomprt", and "TCP" into a more cleanly layered result.
Concretely, this first patch fixes the fact that TCP was basically
acting like a dumping ground needed by the reference backend. This
splits it out, which is fairly mechanical, but touches a lot of lines of
code (basically replacing `tcp` with `refback` and `TCP` with
`RefBackend).
Now, the RefBackend dialect is that dumping ground, which
is slighly better, as it starts allowing TCP to become a nice clean
middle layer that is not related per se to the reference backend.
The previous name RefE2E or "reference e2e flow" was super confusing.
Now that we are seeing more clearly where the "backend" distinction
lies, the [RefBackend] commit tag is born :)
2020-10-07 06:44:18 +08:00
|
|
|
class LowerAllocMemRefOp : public OpRewritePattern<refback::AllocMemRefOp> {
|
2020-05-19 03:50:16 +08:00
|
|
|
public:
|
|
|
|
using OpRewritePattern::OpRewritePattern;
|
[RefBackend] Split out RefBackend (refback) dialect from TCP.
This is the first in a patch series that is refactoring the
constellation of things variously called or associated with "E2E",
"RefE2E", "npcomprt", and "TCP" into a more cleanly layered result.
Concretely, this first patch fixes the fact that TCP was basically
acting like a dumping ground needed by the reference backend. This
splits it out, which is fairly mechanical, but touches a lot of lines of
code (basically replacing `tcp` with `refback` and `TCP` with
`RefBackend).
Now, the RefBackend dialect is that dumping ground, which
is slighly better, as it starts allowing TCP to become a nice clean
middle layer that is not related per se to the reference backend.
The previous name RefE2E or "reference e2e flow" was super confusing.
Now that we are seeing more clearly where the "backend" distinction
lies, the [RefBackend] commit tag is born :)
2020-10-07 06:44:18 +08:00
|
|
|
LogicalResult matchAndRewrite(refback::AllocMemRefOp op,
|
2020-05-19 03:50:16 +08:00
|
|
|
PatternRewriter &rewriter) const override {
|
|
|
|
auto memrefType = op.getType().cast<MemRefType>();
|
|
|
|
auto shape = op.getOperand();
|
|
|
|
// std.alloc only accepts the dynamic extents as operands, so only
|
|
|
|
// collect those.
|
|
|
|
SmallVector<Value, 6> dynamicExtents;
|
|
|
|
for (int i = 0, e = memrefType.getRank(); i < e; i++) {
|
|
|
|
if (memrefType.isDynamicDim(i)) {
|
2020-11-17 05:26:13 +08:00
|
|
|
auto ci = rewriter.create<ConstantIndexOp>(op.getLoc(), i);
|
2020-12-12 06:43:38 +08:00
|
|
|
auto extent = rewriter.create<tensor::ExtractOp>(op.getLoc(), shape,
|
|
|
|
ValueRange({ci}));
|
2020-05-19 03:50:16 +08:00
|
|
|
dynamicExtents.push_back(extent);
|
|
|
|
}
|
|
|
|
}
|
2021-03-24 05:16:23 +08:00
|
|
|
rewriter.replaceOpWithNewOp<memref::AllocOp>(op, memrefType,
|
|
|
|
dynamicExtents);
|
2020-05-19 03:50:16 +08:00
|
|
|
return success();
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
namespace {
|
|
|
|
class LowerAllocMemRefOps
|
|
|
|
: public LowerAllocMemRefOpsBase<LowerAllocMemRefOps> {
|
2020-09-10 15:23:46 +08:00
|
|
|
|
|
|
|
void runOnOperation() override {
|
2020-05-19 03:50:16 +08:00
|
|
|
auto func = getOperation();
|
|
|
|
auto *context = &getContext();
|
2021-03-24 05:16:23 +08:00
|
|
|
RewritePatternSet patterns(context);
|
|
|
|
patterns.add<LowerAllocMemRefOp>(context);
|
2020-05-19 03:50:16 +08:00
|
|
|
ConversionTarget target(*context);
|
[RefBackend] Split out RefBackend (refback) dialect from TCP.
This is the first in a patch series that is refactoring the
constellation of things variously called or associated with "E2E",
"RefE2E", "npcomprt", and "TCP" into a more cleanly layered result.
Concretely, this first patch fixes the fact that TCP was basically
acting like a dumping ground needed by the reference backend. This
splits it out, which is fairly mechanical, but touches a lot of lines of
code (basically replacing `tcp` with `refback` and `TCP` with
`RefBackend).
Now, the RefBackend dialect is that dumping ground, which
is slighly better, as it starts allowing TCP to become a nice clean
middle layer that is not related per se to the reference backend.
The previous name RefE2E or "reference e2e flow" was super confusing.
Now that we are seeing more clearly where the "backend" distinction
lies, the [RefBackend] commit tag is born :)
2020-10-07 06:44:18 +08:00
|
|
|
target.addIllegalOp<refback::AllocMemRefOp>();
|
2020-12-12 06:43:38 +08:00
|
|
|
target.addLegalOp<tensor::ExtractOp>();
|
2021-03-24 05:16:23 +08:00
|
|
|
target.addLegalOp<memref::AllocOp>();
|
2020-08-03 13:06:12 +08:00
|
|
|
target.addLegalOp<ConstantOp>();
|
2020-10-30 06:25:55 +08:00
|
|
|
if (failed(applyPartialConversion(func, target, std::move(patterns)))) {
|
2020-05-19 03:50:16 +08:00
|
|
|
return signalPassFailure();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
};
|
|
|
|
} // namespace
|
|
|
|
|
|
|
|
std::unique_ptr<OperationPass<FuncOp>>
|
|
|
|
mlir::NPCOMP::createLowerAllocMemRefOpsPass() {
|
|
|
|
return std::make_unique<LowerAllocMemRefOps>();
|
|
|
|
}
|
|
|
|
|
2020-05-07 09:41:54 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
2020-10-07 07:14:37 +08:00
|
|
|
// createRefBackendLoweringPipeline
|
2020-05-07 09:41:54 +08:00
|
|
|
//===----------------------------------------------------------------------===//
|
|
|
|
|
2020-10-07 07:14:37 +08:00
|
|
|
void mlir::NPCOMP::createRefBackendLoweringPipeline(
|
|
|
|
OpPassManager &pm, const RefBackendLoweringPipelineOptions &options) {
|
2020-11-07 09:17:28 +08:00
|
|
|
|
2021-09-03 03:47:59 +08:00
|
|
|
// Delete dead lists. RefBackend doesn't support lists, but in some cases
|
|
|
|
// we can get by if all the lists are dead.
|
|
|
|
pm.addNestedPass<FuncOp>(
|
|
|
|
NPCOMP::CommonBackend::createDeleteDeadIREEListsPass());
|
|
|
|
|
2020-11-07 09:17:28 +08:00
|
|
|
// Convert all elementwise ops to linalg.
|
|
|
|
//
|
|
|
|
// Considering correctness, this lets us reuse the linalg bufferization, which
|
|
|
|
// applies uniformly to all linalg structured ops.
|
|
|
|
//
|
|
|
|
// Also, converting to linalg herevopens up a lot of optimization
|
|
|
|
// opportunities.
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createConvertElementwiseToLinalgPass());
|
2020-11-07 09:17:28 +08:00
|
|
|
|
|
|
|
if (options.optimize) {
|
2021-07-24 05:13:19 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createLinalgElementwiseOpFusionPass());
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createCanonicalizerPass());
|
|
|
|
pm.addNestedPass<FuncOp>(createCSEPass());
|
2020-11-07 09:17:28 +08:00
|
|
|
}
|
|
|
|
|
2020-09-17 08:31:40 +08:00
|
|
|
// Lower shape constraints before we enter tensor->memref conversion.
|
2020-09-26 07:02:09 +08:00
|
|
|
// That is, we expand shape.cstr_* ops to eager error handling code.
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createConvertShapeConstraintsPass());
|
2020-09-26 07:02:09 +08:00
|
|
|
// Run shape canonicalizations. In particular, this erases shape.assuming,
|
|
|
|
// now that we have converted shape constraints.
|
2021-08-28 02:31:21 +08:00
|
|
|
// TODO: Don't canonicalize everything.
|
|
|
|
pm.addNestedPass<FuncOp>(createCanonicalizerPass());
|
2020-05-07 09:41:54 +08:00
|
|
|
|
2020-11-17 05:26:13 +08:00
|
|
|
// Lower shape ops to std.
|
|
|
|
pm.addPass(createConvertShapeToStandardPass());
|
|
|
|
|
2020-09-17 08:31:40 +08:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Lower the `tensor` type to `memref`.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// We make a conscious effort here to do this as a sequence of separate passes
|
|
|
|
// rather than a single mega dialect conversion pass.
|
2020-05-12 05:24:57 +08:00
|
|
|
//
|
2020-09-17 08:31:40 +08:00
|
|
|
// This means that intermediate steps have source/target materializations
|
2021-03-24 05:16:23 +08:00
|
|
|
// (memref.tensor_load / memref.buffer_cast) in the IR.
|
2020-09-17 08:31:40 +08:00
|
|
|
|
2020-11-17 05:26:13 +08:00
|
|
|
// Run tensor constant bufferization.
|
|
|
|
// This pass has to run on a module op, and so does the final
|
|
|
|
// FuncBufferizePass. But everything else can run in parallel on functions,
|
|
|
|
// so we try to bracket the entire bufferization pipeline with the module
|
|
|
|
// passes to allow maximum parallelism.
|
2020-11-11 07:14:02 +08:00
|
|
|
pm.addPass(createTensorConstantBufferizePass());
|
[RefBackend] Split out RefBackend (refback) dialect from TCP.
This is the first in a patch series that is refactoring the
constellation of things variously called or associated with "E2E",
"RefE2E", "npcomprt", and "TCP" into a more cleanly layered result.
Concretely, this first patch fixes the fact that TCP was basically
acting like a dumping ground needed by the reference backend. This
splits it out, which is fairly mechanical, but touches a lot of lines of
code (basically replacing `tcp` with `refback` and `TCP` with
`RefBackend).
Now, the RefBackend dialect is that dumping ground, which
is slighly better, as it starts allowing TCP to become a nice clean
middle layer that is not related per se to the reference backend.
The previous name RefE2E or "reference e2e flow" was super confusing.
Now that we are seeing more clearly where the "backend" distinction
lies, the [RefBackend] commit tag is born :)
2020-10-07 06:44:18 +08:00
|
|
|
// refback::AllocMemRefOp takes a shape (i.e. extent tensor) as an argument.
|
|
|
|
// We need to resolve this to std.alloc which takes individual extents.
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createLowerAllocMemRefOpsPass());
|
|
|
|
pm.addNestedPass<FuncOp>(createSCFBufferizePass());
|
2020-11-14 07:34:24 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createLinalgBufferizePass());
|
2020-12-01 10:40:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createStdBufferizePass());
|
2020-12-12 06:43:38 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createTensorBufferizePass());
|
2020-10-28 03:03:12 +08:00
|
|
|
pm.addPass(createFuncBufferizePass());
|
2020-12-01 05:55:29 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createFinalizingBufferizePass());
|
2020-05-16 07:33:01 +08:00
|
|
|
|
2020-10-28 03:03:12 +08:00
|
|
|
// TODO: Do buffer deallocation. We should be able to just drop in the
|
|
|
|
// upstream pass?
|
2020-05-07 09:41:54 +08:00
|
|
|
|
2020-09-17 08:31:40 +08:00
|
|
|
// At this point, we have lots of loose stuff floating around from lowering,
|
|
|
|
// so it's a good time to do some general cleanups.
|
2020-06-02 10:30:13 +08:00
|
|
|
if (options.optimize) {
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createCanonicalizerPass());
|
|
|
|
pm.addNestedPass<FuncOp>(createCSEPass());
|
2020-06-02 10:30:13 +08:00
|
|
|
}
|
2020-05-12 06:27:37 +08:00
|
|
|
|
2020-05-12 09:50:51 +08:00
|
|
|
// --------------------------------------------------------------------------
|
2020-05-21 09:48:53 +08:00
|
|
|
// Preparation for converting to an LLVM module.
|
2020-05-12 09:50:51 +08:00
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Now, we begin the process of lowering to LLVM's level of abstraction
|
|
|
|
// (after which LLVM will take over lowering to machine code).
|
|
|
|
|
|
|
|
// Lower linalg ops to loops.
|
|
|
|
// TODO: Do some linalg optimizations like tiling here.
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createConvertLinalgToLoopsPass());
|
2020-05-12 09:50:51 +08:00
|
|
|
|
2020-07-14 07:15:42 +08:00
|
|
|
// Run a some cleanups.
|
2020-06-02 10:30:13 +08:00
|
|
|
if (options.optimize) {
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createCanonicalizerPass());
|
|
|
|
pm.addNestedPass<FuncOp>(createCSEPass());
|
2020-06-02 10:30:13 +08:00
|
|
|
}
|
2020-05-21 09:48:53 +08:00
|
|
|
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
// Final conversion to an LLVM module.
|
|
|
|
// --------------------------------------------------------------------------
|
|
|
|
|
2020-12-16 04:53:12 +08:00
|
|
|
// Convert affine to std control flow in preparation for going to LLVM.
|
|
|
|
pm.addNestedPass<FuncOp>(createLowerAffinePass());
|
|
|
|
|
2020-05-21 09:48:53 +08:00
|
|
|
// Convert scf to std control flow in preparation for going to LLVM.
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createLowerToCFGPass());
|
2020-05-21 09:48:53 +08:00
|
|
|
|
2020-09-17 08:31:40 +08:00
|
|
|
// Convert functions signatures and other constructs that interface with the
|
2020-10-08 08:12:52 +08:00
|
|
|
// runtime to the `refbackrt` dialect.
|
|
|
|
pm.addPass(createLowerToRefbackrtABIPass());
|
2020-09-17 08:31:40 +08:00
|
|
|
|
2020-05-21 09:48:53 +08:00
|
|
|
// Finally, convert to LLVM dialect using our custom LowerToLLVM pass
|
|
|
|
// which reuses the upstream patterns and gives us a place to add our own
|
2020-10-08 08:12:52 +08:00
|
|
|
// patterns for our own custom ops like the refbackrt ops.
|
2020-05-21 09:48:53 +08:00
|
|
|
pm.addPass(createLowerToLLVMPass());
|
2020-07-14 07:15:42 +08:00
|
|
|
|
|
|
|
// Although LLVM will clean everything up eventually, for the sake of IR
|
|
|
|
// clarity while still in MLIR, run some cleanups.
|
|
|
|
if (options.optimize) {
|
2020-11-11 13:38:13 +08:00
|
|
|
pm.addNestedPass<FuncOp>(createCanonicalizerPass());
|
|
|
|
pm.addNestedPass<FuncOp>(createCSEPass());
|
2020-07-14 07:15:42 +08:00
|
|
|
}
|
2020-05-07 09:41:54 +08:00
|
|
|
}
|