Skip to content

[SandboxIR] Add callbacks for instruction insert/remove/move ops #112965

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

Merged
merged 9 commits into from
Oct 29, 2024

Conversation

slackito
Copy link
Collaborator

No description provided.

@@ -48,6 +61,21 @@ class Context {
/// Type objects.
DenseMap<llvm::Type *, std::unique_ptr<Type, TypeDeleter>> LLVMTypeToTypeMap;

/// Callbacks called when an IR instruction is about to get removed. Keys are
/// used as IDs for deregistration.
DenseMap<int, RemoveInstrCallback> RemoveInstrCallbacks;

Choose a reason for hiding this comment

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

Do you want to keep them as ints or:

using ID = int;

?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Good point, thanks! Switched it to CallbackID.

}

void Context::runInsertInstrCallbacks(Instruction *I) {
for (auto &CBEntry : InsertInstrCallbacks) {

Choose a reason for hiding this comment

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

unnecessary braces

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

Copy link

github-actions bot commented Oct 18, 2024

✅ With the latest revision this PR passed the C/C++ code formatter.

@@ -660,4 +663,57 @@ Module *Context::createModule(llvm::Module *LLVMM) {
return M;
}

void Context::runRemoveInstrCallbacks(Instruction *I) {
for (const auto &CBEntry : RemoveInstrCallbacks) {

Choose a reason for hiding this comment

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

unnecessary braces

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

}

void Context::runMoveInstrCallbacks(Instruction *I, const BBIterator &WhereIt) {
for (auto &CBEntry : MoveInstrCallbacks) {

Choose a reason for hiding this comment

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

unnecessary braces

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Fixed.

- Introduced `CallbackID` typedef for callback ids rather than a plain
  int.
- Remove unnecessary braces.
Copy link
Contributor

@aeubanks aeubanks left a comment

Choose a reason for hiding this comment

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

ultra nit: no period at end of commit title

return ID;
}
void Context::unregisterInsertInstrCallback(CallbackID ID) {
[[maybe_unused]] bool erased = InsertInstrCallbacks.erase(ID);
Copy link
Contributor

Choose a reason for hiding this comment

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

Suggested change
[[maybe_unused]] bool erased = InsertInstrCallbacks.erase(ID);
[[maybe_unused]] bool Erased = InsertInstrCallbacks.erase(ID);

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

done

@slackito slackito changed the title [SandboxIR] Add callbacks for instruction insert/remove/move ops. [SandboxIR] Add callbacks for instruction insert/remove/move ops Oct 18, 2024
- Updated callback (de)registration method signatures in header to use
  the CallbackID type instead of int.

- Corrected case in some variable names to follow the style guide.
std::function<void(Instruction *, const BBIterator &)>;

/// An ID for a registered callback. Used for deregistration.
using CallbackID = int;
Copy link
Contributor

@vporpo vporpo Oct 18, 2024

Choose a reason for hiding this comment

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

Why use an ID and not the raw pointer of the callback itself as the ID ?

Choose a reason for hiding this comment

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

non-determinism

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Pointers to the std::function objects stored in a map/vector can get invalidated when registering/unregistering more callbacks, right?

Copy link
Contributor

Choose a reason for hiding this comment

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

How are we going to get non-determinism ? The ID itself is not printed anywhere, it's just used as a key to remove the callback if needed. If it's the raw pointer of the function it should still work just as well. Or am I missing something?

Copy link
Contributor

Choose a reason for hiding this comment

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

Pointers to the std::function objects stored in a map/vector can get invalidated when registering/unregistering more callbacks, right?

True but we can get around that in multiple ways, one of which is by allocating unique pointers and getting the raw pointer.

What I don't like about the integer IDs is that:

  • they are just being used as a key and they don't really convey anything else. They are not even being used for debugging purposes.
  • they need the static? counter

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

How is another layer of indirection and an extra heap allocation better than keeping an integer around?

Choose a reason for hiding this comment

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

If you use raw pointers as keys in maps, you will get non-determinism.

The context should own the counter.

Copy link
Contributor

Choose a reason for hiding this comment

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

How is another layer of indirection and an extra heap allocation better than keeping an integer around?

Yeah, it's definitely not great, and we should try to make the code that runs the callbacks as fast as possible.

Since @tschuett brought up non-determinism, should we try to make sure that the callbacks are run in the same order as registered? Because we are currently not only not getting that order but since we are iterating over the DenseMap we are also getting a non-deterministic order. Using a MapVector will fix non-determinism, but perhaps we should try to stick to registration order, and try to change it later if it turns out that it's not needed. I think we could use a vector instead of a map, because removal of a callback shouldn't happen too often (and we won't have too many callbacks) so a linear-time search should be fine. Wdyt?

EXPECT_THAT(Inserted, testing::ElementsAre(NewI));
EXPECT_THAT(Removed, testing::IsEmpty());
EXPECT_THAT(Moved, testing::IsEmpty());

Copy link
Member

Choose a reason for hiding this comment

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

A drive-by comment: How about testing insertBefore and insertAfter?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Discussed this offline because @vporpo's prototype only called them on create/erase, but called them insert/remove. We only need create/erase/move callbacks for the vectorizer, so we're only adding those for now, and I have renamed them to create/erase callbacks. This leaves space to add proper insert/remove callbacks in the future if we (or some future user of Sandbox IR) needs them.

Thus, no need to test those here as they have no associated callbacks for now.

Added test to check that registration order is respected when invoking
registered callbacks.
- Remove insert/remove callbacks to create/erase. Don't call the erase
  callback on remove. This way we have a consistent model, but we don't
  have insert/remove callbacks, just create/erase/move. The missing
  callbacks can still be added if needed in the future.

- Changed callback ids to uint64 in the unlikely case they could
  overflow a 32-bit integer in a large compile, causing hard-to-debug
  errors.
@slackito
Copy link
Collaborator Author

Please take another look. I have also switched callback ids from int to uint64_t out of concerns (expressed offline) that we could possibly accidentally overflow a 32-bit integer (maybe registering/deregistering callbacks multiple times per region in a very large compile or something like that).

Copy link
Contributor

@vporpo vporpo left a comment

Choose a reason for hiding this comment

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

Looks good to me.
I think it would make sense to add some assertions on the callback map size.


Context::CallbackID Context::registerEraseInstrCallback(EraseInstrCallback CB) {
CallbackID ID = NextCallbackID++;
EraseInstrCallbacks[ID] = CB;
Copy link
Contributor

Choose a reason for hiding this comment

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

How about an assertion on EraseInstrCallbacks.size() checking that it's smaller than say 16 ? It could help catch issues where you keep on registering callbacks by mistake.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Done. I'm not super happy with having an arbitrary limit like this not documented anywhere and with a kinda low value, but it will be useful while we're developing the vectorizer and we can always revisit this decision later.

@slackito slackito merged commit 4df71ab into llvm:main Oct 29, 2024
8 checks passed
NoumanAmir657 pushed a commit to NoumanAmir657/llvm-project that referenced this pull request Nov 4, 2024
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.

5 participants