This is an automated email from the ASF dual-hosted git repository.
junrushao pushed a commit to branch main
in repository https://gitbox.apache.org/repos/asf/tvm.git
The following commit(s) were added to refs/heads/main by this push:
new 53cee4bca3 [TVMScript] Round-trip DeclBuffer with undefined data
pointer (#14900)
53cee4bca3 is described below
commit 53cee4bca3743a2e26bcb1bfe7aa2aeb3956040d
Author: Eric Lunderberg <[email protected]>
AuthorDate: Sun May 21 22:15:51 2023 -0500
[TVMScript] Round-trip DeclBuffer with undefined data pointer (#14900)
Previously, a `DeclBuffer` object in which `decl_buffer->buffer->data`
had not been defined would be printed without the `data` argument.
This was the same representation as the `Allocate`/`DeclBuffer`
pattern (e.g. `buf = T.decl_buffer(shape,dtype)`), and so the
`Allocate` node would be inserted when parsing.
This commit updates the printing of buffers to be aware of which
variables are implicitly declared in the process of declaring a
buffer. (e.g. A buffer that occurs in `BlockNode::alloc_buffers`
defines both the buffer and the data pointer, while a `DeclBuffer`
only defines the buffer.) This is used to produce a different
representation for the undefined data pointer, removing the ambiguity
with the `Allocate`/`DeclBuffer` sugar.
Because this change only affects malformed TIR in which a `DeclBuffer`
node references an undefined data pointer, it is intended primarily
for debugging purposes.
---
src/script/printer/tir/block.cc | 3 +-
src/script/printer/tir/buffer.cc | 29 +++++++-----
src/script/printer/tir/function.cc | 6 ++-
src/script/printer/tir/stmt.cc | 24 ++++++----
src/script/printer/tir/utils.h | 28 +++++++++++-
.../python/unittest/test_tvmscript_printer_tir.py | 3 +-
tests/python/unittest/test_tvmscript_roundtrip.py | 51 ++++++++++++++++++++++
7 files changed, 121 insertions(+), 23 deletions(-)
diff --git a/src/script/printer/tir/block.cc b/src/script/printer/tir/block.cc
index 0c9289a9d2..178ed4fe75 100644
--- a/src/script/printer/tir/block.cc
+++ b/src/script/printer/tir/block.cc
@@ -180,7 +180,8 @@ Doc PrintBlock(IRDocsifier d, tir::Block block, ObjectPath
block_p, //
tir::Buffer buffer = block->alloc_buffers[i];
ObjectPath buffer_p = block_p->Attr("alloc_buffers")->ArrayIndex(i);
IdDoc lhs = DefineBuffer(buffer, *frame, d);
- ExprDoc rhs = BufferDecl(buffer, "alloc_buffer", {}, buffer_p, *frame, d);
+ ExprDoc rhs = BufferDecl(buffer, "alloc_buffer", {}, buffer_p, *frame, d,
+ BufferVarDefinition::DataPointer);
(*frame)->stmts.push_back(AssignDoc(lhs, rhs, NullOpt));
}
// Step 6. Handle `match_buffer`
diff --git a/src/script/printer/tir/buffer.cc b/src/script/printer/tir/buffer.cc
index ed8b707176..45a0dfd2ae 100644
--- a/src/script/printer/tir/buffer.cc
+++ b/src/script/printer/tir/buffer.cc
@@ -25,7 +25,7 @@ namespace script {
namespace printer {
Map<String, ExprDoc> BufferAttrs(tir::Buffer buffer, const ObjectPath&
buffer_p, const Frame& frame,
- const IRDocsifier& d) {
+ const IRDocsifier& d, BufferVarDefinition
var_definitions) {
using tvm::tir::Var;
using tvm::tir::VarNode;
Map<String, ExprDoc> kwargs;
@@ -93,11 +93,18 @@ Map<String, ExprDoc> BufferAttrs(tir::Buffer buffer, const
ObjectPath& buffer_p,
kwargs.Set("dtype", LiteralDoc::DataType(buffer->dtype,
buffer_p->Attr("dtype")));
}
// Step 3. Handle `buffer.data`
- if (!is_new_var(buffer->data)) {
+ bool is_inline_data = false;
+ if (is_new_var(buffer->data)) {
+ if (var_definitions >= BufferVarDefinition::DataPointer) {
+ is_inline_data = try_inline_def(buffer->data, buffer_p->Attr("data"),
[=]() {
+ return d->AsDoc<ExprDoc>(buffer, buffer_p)->Attr("data");
+ });
+ } else {
+ add_out_of_line_var_def(buffer->data, buffer_p->Attr("data"));
+ }
+ }
+ if (!is_inline_data) {
kwargs.Set("data", d->AsDoc<ExprDoc>(buffer->data,
buffer_p->Attr("data")));
- } else {
- try_inline_def(buffer->data, buffer_p->Attr("data"),
- [=]() { return d->AsDoc<ExprDoc>(buffer,
buffer_p)->Attr("data"); });
}
// Step 4. Handle `buffer.strides`
if (!buffer->strides.empty()) {
@@ -194,15 +201,16 @@ ExprDoc BufferCall(const ExprDoc& prefix, const
Map<String, ExprDoc>& attrs, Arr
}
ExprDoc BufferDecl(const tir::Buffer& buffer, const String& method, const
Array<ExprDoc>& args,
- const ObjectPath& p, const Frame& frame, const IRDocsifier&
d) {
+ const ObjectPath& p, const Frame& frame, const IRDocsifier&
d,
+ BufferVarDefinition var_definitions) {
return BufferCall(/*prefix=*/TIR(d, method),
- /*attrs=*/BufferAttrs(buffer, p, frame, d),
+ /*attrs=*/BufferAttrs(buffer, p, frame, d,
var_definitions),
/*args=*/args);
}
ExprDoc BufferAttn(const tir::Buffer& buffer, const ObjectPath& p, const
Frame& frame,
const IRDocsifier& d) {
- Map<String, ExprDoc> attrs = BufferAttrs(buffer, p, frame, d);
+ Map<String, ExprDoc> attrs = BufferAttrs(buffer, p, frame, d,
BufferVarDefinition::DataPointer);
ExprDoc shape = attrs.Get("shape").value();
ExprDoc dtype =
attrs.Get("dtype").value_or(LiteralDoc::DataType(buffer->dtype,
p->Attr("dtype")));
@@ -281,7 +289,8 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable) //
if (!d->IsVarDefined(buffer)) {
if (Optional<Frame> opt_f = FindLowestVarDef(buffer, d)) {
ExprDoc lhs = DefineBuffer(buffer, opt_f.value(), d);
- ExprDoc rhs = BufferDecl(buffer, "Buffer", {}, p, opt_f.value(), d);
+ ExprDoc rhs = BufferDecl(buffer, "Buffer", {}, p, opt_f.value(), d,
+ BufferVarDefinition::DataPointer);
opt_f.value()->stmts.push_back(AssignDoc(lhs, rhs, NullOpt));
}
}
@@ -298,7 +307,7 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
ExprDoc lhs = DefineBuffer(stmt->buffer, frame, d);
ExprDoc src_buffer = d->AsDoc<ExprDoc>(stmt->source,
p->Attr("source"));
ExprDoc rhs = BufferDecl(stmt->buffer, "match_buffer", {src_buffer},
p->Attr("buffer"),
- d->frames.back(), d);
+ d->frames.back(), d,
BufferVarDefinition::MatchBuffer);
return AssignDoc(lhs, rhs, NullOpt);
});
diff --git a/src/script/printer/tir/function.cc
b/src/script/printer/tir/function.cc
index a8445f23df..19cc67cb14 100644
--- a/src/script/printer/tir/function.cc
+++ b/src/script/printer/tir/function.cc
@@ -119,7 +119,8 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
ExprDoc param_doc = args[i]->lhs;
ObjectPath buffer_p = p->Attr("buffer_map")->MapValue(param);
ExprDoc lhs = DefineBuffer(buffer, *f, d);
- ExprDoc rhs = BufferDecl(buffer, "match_buffer", {param_doc},
buffer_p, *f, d);
+ ExprDoc rhs = BufferDecl(buffer, "match_buffer", {param_doc},
buffer_p, *f, d,
+ BufferVarDefinition::MatchBuffer);
(*f)->stmts.push_back(AssignDoc(lhs, rhs, NullOpt));
}
}
@@ -152,7 +153,8 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
tir::Buffer buffer = root_block->alloc_buffers[i];
ObjectPath buffer_p =
root_block_p->Attr("alloc_buffers")->ArrayIndex(i);
IdDoc lhs = DefineBuffer(buffer, *f, d);
- ExprDoc rhs = BufferDecl(buffer, "alloc_buffer", {}, buffer_p, *f,
d);
+ ExprDoc rhs = BufferDecl(buffer, "alloc_buffer", {}, buffer_p, *f, d,
+ BufferVarDefinition::DataPointer);
(*f)->stmts.push_back(AssignDoc(lhs, rhs, NullOpt));
}
AsDocBody(root_block->body, root_block_p->Attr("body"), f->get(), d);
diff --git a/src/script/printer/tir/stmt.cc b/src/script/printer/tir/stmt.cc
index 384ad6a940..01899d9001 100644
--- a/src/script/printer/tir/stmt.cc
+++ b/src/script/printer/tir/stmt.cc
@@ -126,16 +126,23 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
return WhileDoc(cond, (*f)->stmts);
});
+namespace {
+Doc DeclBufferDoc(tir::DeclBuffer stmt, ObjectPath p, IRDocsifier d,
+ BufferVarDefinition var_definitions) {
+ bool concise = AllowConciseScoping(d);
+ ExprDoc rhs = BufferDecl(stmt->buffer, "decl_buffer", {}, p->Attr("buffer"),
d->frames.back(), d,
+ var_definitions);
+ With<TIRFrame> f(d, stmt);
+ ExprDoc lhs = DefineBuffer(stmt->buffer, *f, d);
+ AsDocBody(stmt->body, p->Attr("body"), f->get(), d);
+ return DoConciseScoping(lhs, rhs, &(*f)->stmts, concise);
+}
+} // namespace
+
TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
.set_dispatch<tir::DeclBuffer>( //
"", [](tir::DeclBuffer stmt, ObjectPath p, IRDocsifier d) -> Doc {
- bool concise = AllowConciseScoping(d);
- ExprDoc rhs =
- BufferDecl(stmt->buffer, "decl_buffer", {}, p->Attr("buffer"),
d->frames.back(), d);
- With<TIRFrame> f(d, stmt);
- ExprDoc lhs = DefineBuffer(stmt->buffer, *f, d);
- AsDocBody(stmt->body, p->Attr("body"), f->get(), d);
- return DoConciseScoping(lhs, rhs, &(*f)->stmts, concise);
+ return DeclBufferDoc(stmt, p, d, BufferVarDefinition::None);
});
TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
@@ -198,7 +205,8 @@ TVM_STATIC_IR_FUNCTOR(IRDocsifier, vtable)
"", [](tir::Allocate stmt, ObjectPath stmt_p, IRDocsifier d) -> Doc {
bool concise = AllowConciseScoping(d);
if (d->cfg->syntax_sugar && IsAllocateDeclBufferPattern(stmt.get()))
{
- return d->AsDoc(stmt->body, stmt_p->Attr("body"));
+ return DeclBufferDoc(Downcast<tir::DeclBuffer>(stmt->body),
stmt_p->Attr("body"), d,
+ BufferVarDefinition::DataPointer);
}
Array<ExprDoc> args;
Array<String> kwargs_keys;
diff --git a/src/script/printer/tir/utils.h b/src/script/printer/tir/utils.h
index cee5fbd0f0..7bdba2b1c6 100644
--- a/src/script/printer/tir/utils.h
+++ b/src/script/printer/tir/utils.h
@@ -177,6 +177,29 @@ inline std::string ReprPrintTIR(const ObjectRef& obj,
const PrinterConfig& cfg)
return Docsify(obj, d, *f, cfg);
}
+/* \brief Specify which variables are defined along with the buffer
+ *
+ * Depending on the context, defining a buffer may define additional
+ * variables associated with the buffer.
+ */
+enum class BufferVarDefinition {
+ // All parameters in the buffer must be defined prior to this call.
+ // For example, DeclBuffer.
+ None,
+
+ // The data pointer is defined along with the buffer, but buffer
+ // parameters (shape/stride/elem_offset) must be defined prior to
+ // use. For example, `BlockNode::alloc_buffers`, or the
+ // syntax-sugar representation of an `Allocate`/`DeclBuffer` pair.
+ DataPointer,
+
+ // The data pointer is defined along with the buffer, along with any
+ // buffer parameters (shape/stride/elem_offset) that have not
+ // previously been defined. For example,
+ // `BlockNode::match_buffers`, or the `PrimFuncNode::buffer_map`.
+ MatchBuffer,
+};
+
/*!
* \brief Declare and define a buffer
* \param buffer The buffer to be defined
@@ -185,10 +208,13 @@ inline std::string ReprPrintTIR(const ObjectRef& obj,
const PrinterConfig& cfg)
* \param p The object path
* \param f The frame
* \param d The IRDocsifier
+ * \param var_definitions Which variables are implicitly defined with
+ * the buffer.
* \return The ExprDoc corresponding to the buffer declaration
*/
ExprDoc BufferDecl(const tir::Buffer& buffer, const String& method, const
Array<ExprDoc>& args,
- const ObjectPath& p, const Frame& frame, const IRDocsifier&
d);
+ const ObjectPath& p, const Frame& frame, const IRDocsifier&
d,
+ BufferVarDefinition var_definitions);
/*!
* \brief Declare and define a buffer as annotation
diff --git a/tests/python/unittest/test_tvmscript_printer_tir.py
b/tests/python/unittest/test_tvmscript_printer_tir.py
index 8427754db7..d78ba70f09 100644
--- a/tests/python/unittest/test_tvmscript_printer_tir.py
+++ b/tests/python/unittest/test_tvmscript_printer_tir.py
@@ -373,7 +373,8 @@ def test_decl_buffer():
_assert_print(
obj,
"""
-with T.decl_buffer((10, 10)) as buffer:
+v = T.handle("float32", "global")
+with T.decl_buffer((10, 10), data=v) as buffer:
T.evaluate(0)
""",
)
diff --git a/tests/python/unittest/test_tvmscript_roundtrip.py
b/tests/python/unittest/test_tvmscript_roundtrip.py
index 7eee601358..2ea7d3ec65 100644
--- a/tests/python/unittest/test_tvmscript_roundtrip.py
+++ b/tests/python/unittest/test_tvmscript_roundtrip.py
@@ -3808,6 +3808,53 @@ def subroutine_call():
return mod
+def undefined_data_ptr_in_decl_buffer():
+ """The T.decl_buffer syntax should not introduce an Allocate
+
+ While T.decl_buffer can be used to represent an
+ Allocate/DeclBuffer pair, performing a round-trip through
+ TVMScript should not introduce an Allocate node.
+ """
+
+ @T.prim_func
+ def func():
+ data_ptr = T.handle("float32")
+ buf = T.decl_buffer(shape=[1], dtype="float32", data=data_ptr)
+ T.evaluate(buf[0])
+
+ return func
+
+
+def undefined_shape_in_decl_buffer():
+ @T.prim_func
+ def func():
+ size = T.int32()
+ buf = T.decl_buffer(shape=[size], dtype="float32")
+ T.evaluate(buf[0])
+
+ return func
+
+
+def undefined_stride_in_decl_buffer():
+ @T.prim_func
+ def func():
+ stride = T.int32()
+ buf = T.decl_buffer(shape=[1], dtype="float32", strides=[stride])
+ T.evaluate(buf[0])
+
+ return func
+
+
+def undefined_elem_offset_in_decl_buffer():
+ @T.prim_func
+ def func():
+ elem_offset = T.int32()
+ buf = T.decl_buffer(shape=[1], dtype="float32",
elem_offset=elem_offset)
+ T.evaluate(buf[0])
+
+ return func
+
+
ir_generator = tvm.testing.parameter(
launch_env_thread,
opt_gemm_normalize,
@@ -3878,6 +3925,10 @@ ir_generator = tvm.testing.parameter(
ir_module_with_attrs,
nested_seqstmt,
subroutine_call,
+ undefined_data_ptr_in_decl_buffer,
+ undefined_shape_in_decl_buffer,
+ undefined_stride_in_decl_buffer,
+ undefined_elem_offset_in_decl_buffer,
)