blob: 25111cec8eda1f9f2fa944a439a799c1442bf49b [file]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
/*!
* \file auto_scheduler_layout_rewrite.h
* \brief Rewrite the layout of "layout free" tensors (e.g., the weight tensors in
* conv2d and dense layers) according to the tile structure generated by the auto-scheduler.
*/
#include "auto_scheduler_layout_rewrite.h"
#include <tvm/relay/attrs/transform.h>
#include <tvm/relay/expr_functor.h>
#include <tvm/relay/op_attr_types.h>
#include <tvm/relay/transform.h>
#include <deque>
#include <functional>
#include <vector>
#include "../backend/te_compiler.h"
#include "pattern_utils.h"
namespace tvm {
namespace relay {
// Two global variables for receiving layout information from python
std::deque<std::string> AutoSchedulerLayoutRewriter::global_ori_layouts_queue;
std::deque<std::string> AutoSchedulerLayoutRewriter::global_new_layouts_queue;
// Copy an Attrs but with a new auto_scheduler_rewritten_layout filed.
template <typename T>
Attrs CopyAttrsWithNewLayout(const T* ptr, const std::string& layout) {
auto n = make_object<T>(*ptr);
n->auto_scheduler_rewritten_layout = layout;
return Attrs(n);
}
// Mutate ops in a function
class FuncMutator : public ExprMutator {
public:
FuncMutator(const std::deque<std::string>& ori_layouts_queue,
const std::deque<std::string>& new_layouts_queue)
: ExprMutator(),
ori_layouts_queue_(ori_layouts_queue),
new_layouts_queue_(new_layouts_queue) {}
Expr VisitExpr_(const CallNode* n) {
auto new_n = ExprMutator::VisitExpr_(n);
const auto* call = new_n.as<CallNode>();
if (call && call->op.as<OpNode>() &&
(std::find(target_ops_.begin(), target_ops_.end(), n->op.as<OpNode>()->name) !=
target_ops_.end()) &&
!ori_layouts_queue_.empty() && !new_layouts_queue_.empty()) {
// Pop a new layout from the queue
const std::string ori_layout = ori_layouts_queue_.front();
const std::string new_layout = new_layouts_queue_.front();
ori_layouts_queue_.pop_front();
new_layouts_queue_.pop_front();
// Insert a new op to do layout transform. (This will be simplified by FoldConstant later).
Expr updated_kernel = MakeAutoSchedulerLayoutTransform(call->args[1], ori_layout, new_layout);
Array<Expr> updated_args = {call->args[0], updated_kernel};
// Update the attrs
Attrs updated_attrs;
if (auto pattr = call->attrs.as<Conv2DAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else if (auto pattr = call->attrs.as<Conv2DWinogradAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else if (auto pattr = call->attrs.as<Conv3DAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else if (auto pattr = call->attrs.as<MatmulAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else if (auto pattr = call->attrs.as<DenseAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else if (auto pattr = call->attrs.as<BatchMatmulAttrs>()) {
updated_attrs = CopyAttrsWithNewLayout(pattr, new_layout);
} else {
LOG(FATAL) << "Unhandled attribute: " << call->attrs;
}
new_n = Call(call->op, updated_args, updated_attrs);
}
return new_n;
}
private:
std::deque<std::string> ori_layouts_queue_;
std::deque<std::string> new_layouts_queue_;
std::vector<std::string> target_ops_{
"nn.conv2d", "nn.conv3d", "nn.contrib_conv2d_winograd_without_weight_transform",
"nn.matmul", "nn.dense", "nn.batch_matmul"};
};
Expr AutoSchedulerLayoutRewriter::VisitExpr_(const CallNode* n) {
auto new_n = ExprMutator::VisitExpr_(n);
if (const auto* call = new_n.as<CallNode>()) {
if (const auto* func = call->op.as<FunctionNode>()) {
global_ori_layouts_queue.clear();
global_new_layouts_queue.clear();
// Use ScheduleGetter to call python lower functions.
// This is used to get the layout transform information.
// The layout transformation will be recorded to global_ori_layout_queue
// and global_new_layouts_queue in ComputeDAG::RewriteLayout.
auto f = runtime::Registry::Get("auto_scheduler.enter_layout_rewrite");
CHECK(f) << "Could not find auto_scheduler.enter_layout_rewrite function.";
(*f)();
tec::PrimFuncFor(GetRef<Function>(func), Target::Current(), GlobalVarSupply(NameSupply("")));
f = runtime::Registry::Get("auto_scheduler.exit_layout_rewrite");
CHECK(f) << "Could not find ansor.exit_layout_rewrite function.";
(*f)();
// Mutate the called function
if (!global_ori_layouts_queue.empty() && !global_new_layouts_queue.empty()) {
auto ret = FuncMutator(global_ori_layouts_queue, global_new_layouts_queue).VisitExpr(new_n);
return ret;
}
}
}
return new_n;
}
Expr AutoSchedulerLayoutRewrite(const Expr& expr) {
return AutoSchedulerLayoutRewriter().Mutate(expr);
}
namespace transform {
Pass AutoSchedulerLayoutRewrite() {
runtime::TypedPackedFunc<Function(Function, IRModule, PassContext)> pass_func =
[=](Function f, IRModule m, PassContext pc) {
return Downcast<Function>(relay::AutoSchedulerLayoutRewrite(f));
};
return CreateFunctionPass(pass_func, 3, "AutoSchedulerLayoutRewrite", {"InferType"});
}
TVM_REGISTER_GLOBAL("relay._transform.AutoSchedulerLayoutRewrite")
.set_body_typed(AutoSchedulerLayoutRewrite);
TVM_REGISTER_GLOBAL("relay.attrs.get_auto_scheduler_rewritten_layout")
.set_body_typed([](const Attrs& attrs) {
if (attrs->IsInstance<Conv2DAttrs>()) {
return attrs.as<Conv2DAttrs>()->auto_scheduler_rewritten_layout;
} else if (attrs->IsInstance<Conv2DWinogradAttrs>()) {
return attrs.as<Conv2DWinogradAttrs>()->auto_scheduler_rewritten_layout;
} else if (attrs->IsInstance<Conv3DAttrs>()) {
return attrs.as<Conv3DAttrs>()->auto_scheduler_rewritten_layout;
} else if (attrs->IsInstance<MatmulAttrs>()) {
return attrs.as<MatmulAttrs>()->auto_scheduler_rewritten_layout;
} else if (attrs->IsInstance<DenseAttrs>()) {
return attrs.as<DenseAttrs>()->auto_scheduler_rewritten_layout;
} else if (attrs->IsInstance<BatchMatmulAttrs>()) {
return attrs.as<BatchMatmulAttrs>()->auto_scheduler_rewritten_layout;
} else {
LOG(FATAL) << "Unhandled attribute: " << attrs;
}
return tvm::String();
});
} // namespace transform
} // namespace relay
} // namespace tvm