Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[TIR] Improve well-formed check's handling of match buffer #16655

Merged
merged 2 commits into from
Mar 14, 2024

Conversation

Lunderberg
Copy link
Contributor

@Lunderberg Lunderberg commented Feb 28, 2024

  • The T.match_buffer at the start of a function may contain repeated use of the same data var. For example, a function that must accept two DLTensor objects with the same backing allocation.

  • The "buffer_bind_scope" is an older style of match buffer, and may be the point of definition for variables.

  • A BufferRealize node may act either as an annotation of an external buffer (which indices are used), or as a point of definition for a local buffer (allocation extents of that buffer).

@Lucien0
Copy link
Contributor

Lucien0 commented Mar 1, 2024

Hi @Lunderberg , I tested my cases with this pr, and they are all working properly now. Thanks for your help!

- The `T.match_buffer` at the start of a function may contain repeated
  use of the same data var.  For example, a function that must accept
  two `DLTensor` objects with the same backing allocation.

- The `"buffer_bind_scope"` is an older style of match buffer, and may
  be the point of definition for variables.
Copy link
Contributor

@slyubomirsky slyubomirsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's good to have a more systematic approach to a feature like points of definition in a MatchBuffer. I commented on some parts of the implementation that I couldn't entirely follow.

if (auto iter_var = op->node.as<IterVar>();
iter_var && (op->attr_key == attr::thread_extent || op->attr_key == attr::virtual_thread)) {
// Some attributes serve as a source of definition for the
// tir::Var they annotate.
context = WithDef(iter_var.value(), path->Attr("node"));
context.push_back(WithDef(iter_var.value(), path->Attr("node")));
} else if (op->attr_key == attr::buffer_bind_scope) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably worth commenting that this acts as an older form of MatchBuffer, per the PR description.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you, and added a comment with description.

@@ -199,12 +174,23 @@ void TIRVisitorWithPath::VisitStmt_(const LetStmtNode* op, ObjectPath path) {
void TIRVisitorWithPath::VisitStmt_(const AttrStmtNode* op, ObjectPath path) {
Visit(op->value, path->Attr("value"));

std::optional<DefContext<IterVar>> context = std::nullopt;
std::vector<std::variant<DefContext<IterVar>, DefContext<Var>>> context;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this ever used? I don't see any reads from it.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There aren't any reads from it, as it holds a scoped context manager. On destruction, the DefContext<T> object removes items from TIRVisitorWithPath::in_scope_definitions_, and calls the ExitDef handler of the child class.

Also, thank you for pointing this one out. When switching from std::optional to std::vector, I forgot to add a while(context.size()) context.pop_back(); loop in case child classes rely on ExitDef being called in the reverse order from EnterDef.

@@ -250,7 +236,8 @@ void TIRVisitorWithPath::VisitStmt_(const BufferStoreNode* op, ObjectPath path)
void TIRVisitorWithPath::VisitStmt_(const BufferRealizeNode* op, ObjectPath path) {
Visit(op->condition, path->Attr("condition"));
Visit(op->bounds, path->Attr("bounds"));
auto context = WithDef(op->buffer, path->Attr("buffer"));
auto context = WithDefIfUndefined(op->buffer->data, path->Attr("buffer")->Attr("data"));
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I imagine this accounts for the case where a BufferRealize can act as a point of definition?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's correct. In cases where the buffer's backing allocation is defined externally, the BufferRealize is an annotation of the bounds where the external buffer is accessed. Otherwise, BufferRealize is an allocation. Prior to this commit, only the external backing allocation was handled.

@@ -195,6 +204,10 @@ void TIRVisitorWithPath::VisitStmt_(const AttrStmtNode* op, ObjectPath path) {
Visit(expr.value(), path->Attr("node"));
}
Visit(op->body, path->Attr("body"));

while (context.size()) {
Copy link
Contributor

@slyubomirsky slyubomirsky Mar 12, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the change. It's probably worth also putting in a comment that this is to ensure that the defs expire, otherwise it seems like spooky action at a distance. Not 100% sure, as the name "DefContext" does imply that it's an RAII sort of thing.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I tried to follow the existing FooContext naming structure (e.g. arith::ConstraintContext).

Copy link
Contributor

@slyubomirsky slyubomirsky left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thank you for the clarifications. The changes seem reasonable and they address issues that have arisen elsewhere.

@Lunderberg Lunderberg merged commit 695f958 into apache:main Mar 14, 2024
21 checks passed
@Lunderberg Lunderberg deleted the tir_well_formed_bugfixes branch March 14, 2024 10:51
thaisacs pushed a commit to thaisacs/tvm that referenced this pull request Apr 3, 2024
)

* [TIR] Improve well-formed check's handling of match buffer

- The `T.match_buffer` at the start of a function may contain repeated
  use of the same data var.  For example, a function that must accept
  two `DLTensor` objects with the same backing allocation.

- The `"buffer_bind_scope"` is an older style of match buffer, and may
  be the point of definition for variables.

* Improved comment, added context.pop_back()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants