blob: 99e98556b6c6fa3f5cee92ee63ba6350e5220c38 [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.
*/
#ifndef TVM_SCRIPT_PRINTER_UTILS_H_
#define TVM_SCRIPT_PRINTER_UTILS_H_
#include <tvm/ffi/cast.h>
#include <tvm/ffi/extra/dataclass.h>
#include <tvm/ffi/extra/json.h>
#include <tvm/ffi/extra/serialization.h>
#include <tvm/ffi/reflection/registry.h>
#include <tvm/runtime/base.h>
#include <tvm/runtime/logging.h>
#include <tvm/script/printer/ir_docsifier.h>
#include <tvm/script/printer/printer.h>
#include <sstream>
#include <string>
#include <unordered_set>
#include <utility>
#include <vector>
#include "../../support/str_escape.h"
namespace tvm {
namespace script {
namespace printer {
// Note: the `TVM_SCRIPT_REPR` macro is intentionally duplicated in each
// dialect-local `src/<dialect>/script/printer/utils.h`. Keeping a single
// definition here would force the dialect headers to depend on this shared
// header, which the per-dialect restructure aims to avoid for cross-directory
// references. See each `<dialect>/script/printer/utils.h` for the macro.
inline std::string Docsify(const ffi::ObjectRef& obj, const IRDocsifier& d, const Frame& f,
const PrinterConfig& cfg) {
Doc doc = d->AsDoc(obj, AccessPath::Root());
bool move_source_paths = false;
if (const auto* expr_doc = doc.as<ExprDocNode>()) {
if (!cfg->verbose_expr) {
f->stmts.clear();
}
f->stmts.push_back(ExprStmtDoc(ffi::GetRef<ExprDoc>(expr_doc)));
} else if (const auto* stmt_doc = doc.as<StmtDocNode>()) {
f->stmts.push_back(ffi::GetRef<StmtDoc>(stmt_doc));
} else if (const auto* stmt_block = doc.as<StmtBlockDocNode>()) {
for (const StmtDoc& d : stmt_block->stmts) {
f->stmts.push_back(d);
}
move_source_paths = true;
} else {
TVM_FFI_THROW(TypeError) << "Unexpected doc type: " << doc->GetTypeKey();
}
std::ostringstream os;
if (!d->metadata.empty()) {
if (d->cfg->show_meta) {
os << "metadata = tvm.ir.load_json(\"\"\""
<< support::StrEscape(
ffi::json::Stringify(
ffi::ToJSONGraph(
ffi::Map<ffi::String, ffi::Any>(d->metadata.begin(), d->metadata.end()),
ffi::json::Object{{"tvm_version", TVM_VERSION}}),
2),
false, false)
<< "\"\"\")\n";
} else {
f->stmts.push_back(
CommentDoc("Metadata omitted. Use show_meta=True in script() method to show it."));
}
}
if (move_source_paths) {
StmtBlockDoc new_doc(f->stmts);
new_doc->source_paths = std::move(doc->source_paths);
os << DocToPythonScript(new_doc, cfg);
} else {
os << DocToPythonScript(StmtBlockDoc(f->stmts), cfg);
}
return os.str();
}
/*! \brief Creates the IR common prefix, which is by default `I` */
inline ExprDoc IR(const IRDocsifier& d, const ffi::String& attr) {
d->ir_usage.insert("ir");
return IdDoc(d->cfg->ir_prefix)->Attr(attr);
}
/*! \brief Creates the TIR common prefix, which is by default `T` */
inline ExprDoc TIR(const IRDocsifier& d, const ffi::String& attr) {
d->ir_usage.insert("tirx");
return IdDoc(d->cfg->GetExtraConfig<ffi::String>("tirx.prefix", "T"))->Attr(attr);
}
/*! \brief Alias for TIR — historical TIRx name used by tirx printer code */
inline ExprDoc TIRx(const IRDocsifier& d, const ffi::String& attr) { return TIR(d, attr); }
/*! \brief Creates the Relax common prefix, which is by default `R` */
inline ExprDoc Relax(const IRDocsifier& d, const ffi::String& attr) {
d->ir_usage.insert("relax");
return IdDoc(d->cfg->GetExtraConfig<ffi::String>("relax.prefix", "R"))->Attr(attr);
}
inline std::string DType2Str(DLDataType dtype) {
return (((dtype).code == kDLOpaqueHandle) && ((dtype).bits == 0) && ((dtype).lanes == 0))
? "void"
: ffi::DLDataTypeToString(dtype);
}
/*! \brief Add headers as comments to doc if needed */
inline Doc HeaderWrapper(const IRDocsifier& d, const Doc& doc) {
if (d->ir_usage.size()) {
ffi::Array<StmtDoc> stmts;
if (d->ir_usage.count("ir")) {
stmts.push_back(CommentDoc("from tvm.script import ir as " + d->cfg->ir_prefix));
}
if (d->ir_usage.count("tirx")) {
stmts.push_back(CommentDoc("from tvm.script import tirx as " +
d->cfg->GetExtraConfig<ffi::String>("tirx.prefix", "T")));
// Layout sugar like `4 @ Axis.laneid` references registered axes via the
// `Axis` class attribute. Mirror the `Axis` injection in `_default_globals`
// so readers see the dependency. Decorative only.
stmts.push_back(CommentDoc("from tvm.tirx.layout import Axis"));
}
if (d->ir_usage.count("relax")) {
stmts.push_back(CommentDoc("from tvm.script import relax as " +
d->cfg->GetExtraConfig<ffi::String>("relax.prefix", "R")));
}
stmts.push_back(CommentDoc(""));
stmts.push_back(doc.as_or_throw<StmtDoc>());
return StmtBlockDoc(stmts);
}
return doc;
}
/*! \brief Check if a string has multiple lines. */
inline bool HasMultipleLines(const std::string& str) {
return str.find_first_of('\n') != std::string::npos;
}
inline ffi::Optional<ffi::String> GetBindingName(const IRDocsifier& d) {
return d->cfg->binding_names.empty() ? ffi::Optional<ffi::String>(std::nullopt)
: d->cfg->binding_names.back();
}
inline ffi::Optional<ffi::String> FindFunctionName(const IRDocsifier& d, const BaseFunc& f) {
if (ffi::Optional<ffi::String> name = GetBindingName(d)) {
return name.value();
}
if (ffi::Optional<ffi::String> sym = f->GetAttr<ffi::String>(tvm::attr::kGlobalSymbol)) {
return sym.value();
}
return std::nullopt;
}
inline ffi::String GenerateUniqueName(std::string name_hint,
const std::unordered_set<ffi::String>& defined_names) {
for (char& c : name_hint) {
if (c != '_' && !std::isalnum(c)) {
c = '_';
}
}
std::string name = name_hint;
for (int i = 1; defined_names.count(name) > 0; ++i) {
name = name_hint + "_" + std::to_string(i);
}
return name;
}
} // namespace printer
} // namespace script
} // namespace tvm
#endif // TVM_SCRIPT_PRINTER_UTILS_H_