Skip to content

Support Backend Specific Verifiers #3469

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

Closed
wants to merge 2 commits into from

Conversation

shajrawi
Copy link
Contributor

Summary:
Adds support for backend-specific verifiers to Glow.
I removed the asserts in the backend specific code when executing Nodes with components that depend on the backend for validity and added verifiers to take their place.
There are two backend-specific verifiers: one that works on Function and one that takes in IRFunction - this allows verification at different stages of the Glow pipeline.
I also added two error types: one that indicates a verification error at initial IR generation (COMPILE_UNSUPPORTED_IR_AFTER_GENERATE) and one that indicates the faulty code appeared after IR optimizations (COMPILE_UNSUPPORTED_IR_AFTER_OPTIMIZE).
I also refactored the code, such that Graph Optimizer does not check that all nodes are supported directly, that should not be its job, it calls the verifier instead. The base method for the Function verifier does said check.

Fixes #3450

Test Plan:
ninja test

@shajrawi shajrawi force-pushed the backend_verify branch 2 times, most recently from b1f72f3 to daa3eb3 Compare August 28, 2019 22:28
Copy link
Contributor

@jfix71 jfix71 left a comment

Choose a reason for hiding this comment

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

Looks pretty good to me! Just a couple minor nits, and agree would be nice to reduce the boilerplate stuff.

@opti-mix
Copy link
Contributor

@shajrawi Overall, looks pretty good! Thanks for adding this feature!

@ayermolo
Copy link
Contributor

Somewhat tangential, but right now isSupported is used for two things.

  1. Verify that this node is legal for specific backend.
  2. During quantization to determine if node can be converted to quantized.

To me it seems that it should be two different functions to answer those two questions.

@jfix71
Copy link
Contributor

jfix71 commented Aug 28, 2019

@shajrawi Can you also update docs/Backends.md?

@jfix71
Copy link
Contributor

jfix71 commented Aug 28, 2019

@ayermolo The check for if a node is supported as quantized is a subset of isOpSupported(). If we moved them to two separate functions we'd likely duplicate a lot of code. Though I believe we'd also be able to pass in a Node directly into isOpSupported() instead of NodeInfo, allowing for checking members and actual input/output Nodes instead of just input/output types.

@ayermolo
Copy link
Contributor

Well isOpSupported can always call canQuantize (or whatever the other function name ends up). This way BE can control which nodes get quantized type, and also do additional checks on generic nodes to make sure they conform to BE requirements.

Although I guess with ability to override verify the BE will be able to do it anyway after the check for isOpSupported.

@shajrawi
Copy link
Contributor Author

@jfix71 I resolved the minor nits and updated docs/Backends.md
@opti-mix , @nickgg I created templated solution for most grievous repetition, as a bonus my solution checks for fusion (OpenCL and Interpreter backends) and NHWC layout (Interpreter backend) for all ALL Node(s) and Instruction(s) that contain layout / fusion fields without specifying them directly. making it future proof :)

@shajrawi shajrawi force-pushed the backend_verify branch 2 times, most recently from 311bcb4 to 36f193b Compare August 29, 2019 04:52
Copy link
Contributor

@opti-mix opti-mix left a comment

Choose a reason for hiding this comment

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

Thanks for introducing those helpers! Looks better now.

@@ -173,6 +173,46 @@ bool isOutput(const Placeholder *PH, const IRFunction &F);
/// by the current function.
bool isInput(const Placeholder *PH, const IRFunction &F);

/// Checks if typename C contains getFusedActivation() method
Copy link
Contributor

Choose a reason for hiding this comment

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

May be add "Use template meta-programming to check if ..."

Copy link
Contributor

Choose a reason for hiding this comment

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

Using this approach is very clever! May be too clever ;-)

@@ -3017,5 +2997,11 @@ llvm::Error glow::optimizeFunction(Function *F, const Backend &B,
::glow::optimize(F, cctx, B);
}

return checkAllNodesSupported(*F, B);
if (!B.verify(*F)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

This deserves some comment IMHO. If I understand correctly, our strategy here is that we start using backend specific verification only after transformPostLowering, i.e. only after some backend-specific nodes were introduced eventually. Is it a correct understanding? Please explain it in the comment then.

But I've got another related question here:

  • We may call some graph optimizations multiple times. And we may call verify after each pass IIRC (see the pass manager details). Which verifier is currently called after each pass though and which one should be called? The backend-independent one or the backend-specific one? Or should it depend on the stage of compilation (e.g. call the backend-independent one before transformPostLowering and the backend-specific one after transformPostLowering)? WDYT?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Excellent observation! I changed this behavior and made it explicit in a separate commit that I added to this PR!

We should always do backend independent verifications of course, because we don't duplicate work, however, we should also do backend specific verifications form the moment we have the function in a lowered state.

The pass manager now checks the function state and adds extra verifications appropriately.

Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I can follow the logic of this new commit. Some of the optimizations are essentially backend-independent. But it seems like you demand now to always pass a Backend to the pass manager, even if the pass manager does not use it at the given stage of compilation. As a result you had to touch a bunch of places and pass a fake Backend as a parameter now. This seems odd to me. And if this backend is always used (may be I overlooked something), which concrete Backend would you use before introducing target-specific nodes? Looks like you'd need a default Glow backend, which just captures the default Glow requirements in this case.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I updated the the follow-up commit to avoid that. we now pass a pointer instead of a reference, with a default value of nullptr, so we no longer need to pollute the code base with the fake backend. I think we only need to pass the backend when we are doing backend specific optimizations. WDYT?

Copy link
Contributor

Choose a reason for hiding this comment

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

Yess, makes sense.

@shajrawi shajrawi force-pushed the backend_verify branch 2 times, most recently from 8883258 to 596b7a2 Compare August 29, 2019 17:44
@@ -3017,5 +2997,11 @@ llvm::Error glow::optimizeFunction(Function *F, const Backend &B,
::glow::optimize(F, cctx, B);
}

return checkAllNodesSupported(*F, B);
if (!B.verify(*F)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

I'm not sure I can follow the logic of this new commit. Some of the optimizations are essentially backend-independent. But it seems like you demand now to always pass a Backend to the pass manager, even if the pass manager does not use it at the given stage of compilation. As a result you had to touch a bunch of places and pass a fake Backend as a parameter now. This seems odd to me. And if this backend is always used (may be I overlooked something), which concrete Backend would you use before introducing target-specific nodes? Looks like you'd need a default Glow backend, which just captures the default Glow requirements in this case.

Copy link
Contributor

@jfix71 jfix71 left a comment

Choose a reason for hiding this comment

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

Looks pretty good to me, just a series of nits.

@shajrawi
Copy link
Contributor Author

Amended the PR to address the nits :)

Copy link
Contributor

@opti-mix opti-mix left a comment

Choose a reason for hiding this comment

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

Looks pretty-good now!

I'm still thinking about the condition Backend verifications is always a super-set of F->verify(). Is it really the case under all circumstances?

@@ -3017,5 +2997,11 @@ llvm::Error glow::optimizeFunction(Function *F, const Backend &B,
::glow::optimize(F, cctx, B);
}

return checkAllNodesSupported(*F, B);
if (!B.verify(*F)) {
Copy link
Contributor

Choose a reason for hiding this comment

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

Yess, makes sense.

return checkAllNodesSupported(F);
}

bool Backend::verify(const IRFunction &IR) const { return true; }
Copy link
Contributor

Choose a reason for hiding this comment

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

Maybe it's better to make this pure virtual to force BE to deal with it, versus thinking it does something when it doesn't?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I agree. Would like to turn them into pure virtual, but, I don't want to break any backends when doing this commit + I will intend to create a beginner / good first issue for someone to do write a verifier for the Habana backend.

@shajrawi
Copy link
Contributor Author

@opti-mix Fixed the nits + I made an explicit call to the target independent verifier to make sure.

@shajrawi shajrawi force-pushed the backend_verify branch 2 times, most recently from 927b9ad to 634115a Compare August 29, 2019 22:55
@shajrawi
Copy link
Contributor Author

Fixed the nits / updated the comments. and all the tests pass.

Copy link
Contributor

@opti-mix opti-mix 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! Great job!

Just address the final minor grammar nits (ideally, without introducing new ones ;-) and ship it!
:shipit: 🚀

@opti-mix
Copy link
Contributor

👏 🎆

Copy link

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shajrawi has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

Copy link

@facebook-github-bot facebook-github-bot left a comment

Choose a reason for hiding this comment

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

@shajrawi has imported this pull request. If you are a Facebook employee, you can view this diff on Phabricator.

@facebook-github-bot
Copy link

@shajrawi merged this pull request in 46dcb19.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

Successfully merging this pull request may close these issues.

Support Backend Specific Node Verifiers
6 participants