blob: f270bdae9f6a8296ade2e4abf34e3d3fe6410c57 [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.
*/
#include <tvm/ffi/cast.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/module.h>
#include <tvm/script/ir_builder/base.h>
#include <utility>
namespace tvm {
namespace script {
namespace ir_builder {
namespace {
bool PositionLessEqual(int lhs_line, int lhs_column, int rhs_line, int rhs_column) {
return lhs_line < rhs_line || (lhs_line == rhs_line && lhs_column <= rhs_column);
}
bool Contains(const Span& outer, const Span& inner) {
if (!outer.defined() || !inner.defined() || outer.as<SequentialSpanNode>() ||
inner.as<SequentialSpanNode>() || !outer->source_name.same_as(inner->source_name)) {
return false;
}
return PositionLessEqual(outer->line, outer->column, inner->line, inner->column) &&
PositionLessEqual(inner->end_line, inner->end_column, outer->end_line, outer->end_column);
}
void AppendNormalizedSpan(const Span& span, std::vector<Span>* normalized) {
if (!span.defined()) {
return;
}
if (const auto* sequential = span.as<SequentialSpanNode>()) {
for (const Span& nested : sequential->spans) {
AppendNormalizedSpan(nested, normalized);
}
return;
}
if (!normalized->empty() && Contains(normalized->back(), span)) {
normalized->back() = span;
} else {
normalized->push_back(span);
}
}
} // namespace
TVM_FFI_STATIC_INIT_BLOCK() {
IRBuilderFrameNode::RegisterReflection();
IRBuilderNode::RegisterReflection();
}
void IRBuilderFrameNode::EnterWithScope() {
IRBuilder::Current()->frames.push_back(ffi::GetRef<IRBuilderFrame>(this));
}
void IRBuilderFrameNode::ExitWithScope() {
for (auto it = callbacks.rbegin(); it != callbacks.rend(); ++it) {
(*it)();
}
this->callbacks.clear();
IRBuilder::Current()->frames.pop_back();
}
void IRBuilderFrameNode::AddCallback(ffi::TypedFunction<void()> callback) {
if (IRBuilder::Current()->frames.empty()) {
TVM_FFI_THROW(InternalError) << "ValueError: No frames in Builder to add callback";
}
IRBuilder::Current()->frames.back()->callbacks.push_back(callback);
}
IRBuilder::IRBuilder() {
ffi::ObjectPtr<IRBuilderNode> n = ffi::make_object<IRBuilderNode>();
n->frames.clear();
n->result = std::nullopt;
n->source_spans.clear();
data_ = n;
}
void IRBuilderNode::PushSourceSpan(Span span) {
TVM_FFI_CHECK(span.defined(), ValueError) << "ValueError: Cannot push an undefined source span";
source_spans.push_back(std::move(span));
}
void IRBuilderNode::PopSourceSpan() {
TVM_FFI_CHECK(!source_spans.empty(), ValueError)
<< "ValueError: No source span exists in the builder scope";
source_spans.pop_back();
}
Span IRBuilderNode::GetCurrentSourceSpan() const {
std::vector<Span> normalized;
normalized.reserve(source_spans.size());
for (const Span& span : source_spans) {
AppendNormalizedSpan(span, &normalized);
}
if (normalized.empty()) {
return Span();
}
if (normalized.size() == 1) {
return normalized[0];
}
ffi::Array<Span> spans;
spans.reserve(normalized.size());
for (const Span& span : normalized) {
spans.push_back(span);
}
return SequentialSpan(std::move(spans));
}
ffi::ObjectRef IRBuilderNode::SetCurrentSourceSpan(ffi::ObjectRef obj) const {
Span span = GetCurrentSourceSpan();
if (span.defined()) {
if (const auto* expr = obj.as<ExprNode>(); expr != nullptr && !expr->span.defined()) {
expr->span = std::move(span);
}
}
return obj;
}
std::vector<IRBuilder>* ThreadLocalBuilderStack() {
thread_local std::vector<IRBuilder> stack;
return &stack;
}
void IRBuilder::EnterWithScope() {
IRBuilderNode* n = this->get();
TVM_FFI_CHECK(n->frames.empty(), ValueError)
<< "ValueError: There are frame(s) left in the builder: " << n->frames.size()
<< ". Please use a fresh new builder every time building IRs";
TVM_FFI_CHECK(n->source_spans.empty(), ValueError)
<< "ValueError: There are source span(s) left in the builder: " << n->source_spans.size()
<< ". Please use a fresh new builder every time building IRs";
n->result = std::nullopt;
std::vector<IRBuilder>* stack = ThreadLocalBuilderStack();
stack->push_back(*this);
}
void IRBuilder::ExitWithScope() {
std::vector<IRBuilder>* stack = ThreadLocalBuilderStack();
TVM_FFI_ICHECK(!stack->empty());
stack->pop_back();
}
IRBuilder IRBuilder::Current() {
std::vector<IRBuilder>* stack = ThreadLocalBuilderStack();
TVM_FFI_CHECK(!stack->empty(), ValueError) << "ValueError: No builder in current scope";
return stack->back();
}
bool IRBuilder::IsInScope() {
std::vector<IRBuilder>* stack = ThreadLocalBuilderStack();
return !stack->empty();
}
namespace details {
Namer::FType& Namer::vtable() {
static FType inst;
return inst;
}
void Namer::Name(ffi::ObjectRef node, ffi::String name) {
static const FType& f = vtable();
TVM_FFI_CHECK(node.defined(), ValueError) << "ValueError: Cannot name nullptr with: " << name;
TVM_FFI_CHECK(f.can_dispatch(node), ValueError)
<< "ValueError: Do not know how to name type \"" << node->GetTypeKey() << "\"";
f(node, name);
}
} // namespace details
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def_method("script.ir_builder.IRBuilderFrameEnter", &IRBuilderFrameNode::EnterWithScope)
.def_method("script.ir_builder.IRBuilderFrameExit", &IRBuilderFrameNode::ExitWithScope)
.def_method("script.ir_builder.IRBuilderFrameAddCallback", &IRBuilderFrameNode::AddCallback)
.def("script.ir_builder.IRBuilder", []() { return IRBuilder(); })
.def_method("script.ir_builder.IRBuilderEnter", &IRBuilder::EnterWithScope)
.def_method("script.ir_builder.IRBuilderExit", &IRBuilder::ExitWithScope)
.def("script.ir_builder.IRBuilderCurrent", IRBuilder::Current)
.def("script.ir_builder.IRBuilderIsInScope", IRBuilder::IsInScope)
.def_method("script.ir_builder.IRBuilderGet", &IRBuilderNode::Get<ffi::ObjectRef>)
.def_method("script.ir_builder.IRBuilderPushSourceSpan", &IRBuilderNode::PushSourceSpan)
.def_method("script.ir_builder.IRBuilderPopSourceSpan", &IRBuilderNode::PopSourceSpan)
.def_method("script.ir_builder.IRBuilderSetCurrentSourceSpan",
&IRBuilderNode::SetCurrentSourceSpan)
.def("script.ir_builder.IRBuilderName", IRBuilder::Name<ffi::ObjectRef>);
}
} // namespace ir_builder
} // namespace script
} // namespace tvm