blob: 983cfd2a8244b36656da245970960e95d1a27463 [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/extra/dataclass.h>
#include <tvm/ffi/function.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/ir/expr.h>
#include <tvm/script/printer/printer.h>
#include <algorithm>
#include <sstream>
namespace tvm {
namespace {
std::string RenderFallbackWithInvisiblePathInfo(const ffi::String& script,
const PrinterConfig& config) {
if (!config->render_invisible_path_info || config->path_to_underline.empty()) {
return std::string(script);
}
std::ostringstream os;
for (size_t i = 0; i < config->path_to_underline.size(); ++i) {
if (i != 0) os << "\n";
os << "Access path: " << config->path_to_underline[i]
<< "\nNote: No visible object for this path is rendered in TVMScript.";
}
os << "\n\n" << script;
return os.str();
}
} // namespace
TVM_FFI_STATIC_INIT_BLOCK() { PrinterConfigNode::RegisterReflection(); }
TVMScriptPrinter::FType& TVMScriptPrinter::vtable() {
static FType inst;
return inst;
}
std::string Script(const ffi::ObjectRef& node, const ffi::Optional<PrinterConfig>& cfg) {
PrinterConfig config = cfg.value_or(PrinterConfig());
if (!TVMScriptPrinter::vtable().can_dispatch(node)) {
// Fall back to ffi::ReprPrint for types not registered with TVMScriptPrinter.
return RenderFallbackWithInvisiblePathInfo(ffi::ReprPrint(ffi::Any(node)), config);
}
return TVMScriptPrinter::vtable()(node, config);
}
bool IsIdentifier(const std::string& name) {
// Python identifiers follow the regex: "^[a-zA-Z_][a-zA-Z0-9_]*$"
// `std::regex` would cause a symbol conflict with PyTorch, we avoids to use it in the codebase.
//
// We convert the regex into following conditions:
// 1. The name is not empty.
// 2. The first character is either an alphabet or an underscore.
// 3. The rest of the characters are either an alphabet, a digit or an underscore.
return name.size() > 0 && //
(std::isalpha(name[0]) || name[0] == '_') && //
std::all_of(name.begin() + 1, name.end(),
[](char c) { return std::isalnum(c) || c == '_'; });
}
PrinterConfig::PrinterConfig(ffi::Map<ffi::String, Any> config_dict) {
ffi::ObjectPtr<PrinterConfigNode> n = ffi::make_object<PrinterConfigNode>();
if (auto v = config_dict.Get("name")) {
n->binding_names.push_back(v.value().as_or_throw<ffi::String>());
}
if (auto v = config_dict.Get("show_meta")) {
n->show_meta = v.value().cast<bool>();
}
if (auto v = config_dict.Get("ir_prefix")) {
n->ir_prefix = v.value().as_or_throw<ffi::String>();
}
if (auto v = config_dict.Get("module_alias")) {
n->module_alias = v.value().as_or_throw<ffi::String>();
}
if (auto v = config_dict.Get("buffer_dtype")) {
n->buffer_dtype = ffi::StringToDLDataType(v.value().as_or_throw<ffi::String>());
}
if (auto v = config_dict.Get("int_dtype")) {
n->int_dtype = ffi::StringToDLDataType(v.value().as_or_throw<ffi::String>());
}
if (auto v = config_dict.Get("float_dtype")) {
n->float_dtype = ffi::StringToDLDataType(v.value().as_or_throw<ffi::String>());
}
if (auto v = config_dict.Get("verbose_expr")) {
n->verbose_expr = v.value().cast<bool>();
}
if (auto v = config_dict.Get("indent_spaces")) {
n->indent_spaces = v.value().cast<int>();
}
if (auto v = config_dict.Get("print_line_numbers")) {
n->print_line_numbers = v.value().cast<bool>();
}
if (auto v = config_dict.Get("num_context_lines")) {
n->num_context_lines = v.value().cast<int>();
}
if (auto v = config_dict.Get("path_to_underline")) {
n->path_to_underline =
v.value().as_or_throw<ffi::Optional<ffi::Array<ffi::reflection::AccessPath>>>().value_or(
ffi::Array<ffi::reflection::AccessPath>());
}
if (auto v = config_dict.Get("path_to_annotate")) {
n->path_to_annotate =
v.value()
.as_or_throw<ffi::Optional<ffi::Map<ffi::reflection::AccessPath, ffi::String>>>()
.value_or(ffi::Map<ffi::reflection::AccessPath, ffi::String>());
}
if (auto v = config_dict.Get("obj_to_underline")) {
n->obj_to_underline =
v.value().as_or_throw<ffi::Optional<ffi::Array<ffi::ObjectRef>>>().value_or(
ffi::Array<ffi::ObjectRef>());
}
if (auto v = config_dict.Get("obj_to_annotate")) {
n->obj_to_annotate =
v.value().as_or_throw<ffi::Optional<ffi::Map<ffi::ObjectRef, ffi::String>>>().value_or(
ffi::Map<ffi::ObjectRef, ffi::String>());
}
if (auto v = config_dict.Get("syntax_sugar")) {
n->syntax_sugar = v.value().cast<bool>();
}
if (auto v = config_dict.Get("show_object_address")) {
n->show_object_address = v.value().cast<bool>();
}
if (auto v = config_dict.Get("render_invisible_path_info")) {
n->render_invisible_path_info = v.value().cast<bool>();
}
// Dialect-specific keys are stored in extra_config with dotted-name keys.
// String-typed dialect keys passed through directly.
for (const char* key : {"tirx.prefix", "relax.prefix"}) {
if (auto v = config_dict.Get(key)) {
n->extra_config.Set(ffi::String(key), v.value());
}
}
// Boolean dialect keys.
if (auto v = config_dict.Get("relax.show_all_ty")) {
n->extra_config.Set(ffi::String("relax.show_all_ty"), v.value());
}
if (auto v = config_dict.Get("extra_config")) {
auto extra = v.value().as_or_throw<ffi::Map<ffi::String, ffi::Any>>();
for (auto kv : extra) {
n->extra_config.Set(kv.first, kv.second);
}
if (auto render = extra.Get("render_invisible_path_info")) {
n->render_invisible_path_info = render.value().cast<bool>();
}
}
// Checking prefixes if they are valid Python identifiers.
TVM_FFI_ICHECK(IsIdentifier(std::string(n->ir_prefix)))
<< "Invalid `ir_prefix`: " << n->ir_prefix;
ffi::String tir_prefix = n->GetExtraConfig<ffi::String>("tirx.prefix", "T");
ffi::String relax_prefix = n->GetExtraConfig<ffi::String>("relax.prefix", "R");
TVM_FFI_ICHECK(IsIdentifier(std::string(tir_prefix))) << "Invalid `tirx.prefix`: " << tir_prefix;
TVM_FFI_ICHECK(IsIdentifier(std::string(relax_prefix)))
<< "Invalid `relax.prefix`: " << relax_prefix;
TVM_FFI_ICHECK(n->module_alias.empty() || IsIdentifier(std::string(n->module_alias)))
<< "Invalid `module_alias`: " << n->module_alias;
this->data_ = std::move(n);
}
ffi::Array<ffi::String> PrinterConfigNode::GetBuiltinKeywords() {
ffi::String tir_prefix = GetExtraConfig<ffi::String>("tirx.prefix", "T");
ffi::String relax_prefix = GetExtraConfig<ffi::String>("relax.prefix", "R");
ffi::Array<ffi::String> result{this->ir_prefix, tir_prefix, relax_prefix};
if (!this->module_alias.empty()) {
result.push_back(this->module_alias);
}
return result;
}
TVM_FFI_STATIC_INIT_BLOCK() {
namespace refl = tvm::ffi::reflection;
refl::GlobalDef()
.def("node.PrinterConfig",
[](ffi::Map<ffi::String, Any> config_dict) { return PrinterConfig(config_dict); })
.def("node.TVMScriptPrinterScript", tvm::Script);
}
} // namespace tvm