Skip to content

Add mark-entry-points pass to annotate nested-module entry functions#2899

Open
mehrdad2m wants to merge 6 commits into
mainfrom
mark-entry-points
Open

Add mark-entry-points pass to annotate nested-module entry functions#2899
mehrdad2m wants to merge 6 commits into
mainfrom
mark-entry-points

Conversation

@mehrdad2m

Copy link
Copy Markdown
Contributor

Context:
When the compiler emits nested modules, the functions that the host actually dispatches into need to be distinguishable from purely-internal helpers. Downstream passes may use this as a signal for "this function is an externally-reachable entry point".

Description of the Change:
Introduces a catalyst.entry_point annotation marking the functions that are externally callable, produced from either:

  1. frontend: for the main dispatch function
  2. MarkEntryPoints pass for nested modules.

Benefits:

Possible Drawbacks:

Related GitHub Issues:

@mehrdad2m mehrdad2m requested a review from a team June 1, 2026 17:44
@dime10

dime10 commented Jun 1, 2026

Copy link
Copy Markdown
Contributor

Thanks @mehrdad2m! I have a couple of questions:

  • Do we want to use the same attribute for the program entry point (analogous to a main function), as for the quantum kernel entry points?
  • For the kernels, is there a reason that this is more suitable as a pass rather than an attribute assigned during lowering?

@codecov

codecov Bot commented Jun 1, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.00%. Comparing base (361109c) to head (3fde5cb).
⚠️ Report is 5 commits behind head on main.

Additional details and impacted files
@@           Coverage Diff           @@
##             main    #2899   +/-   ##
=======================================
  Coverage   97.00%   97.00%           
=======================================
  Files         167      167           
  Lines       18875    18876    +1     
  Branches     1773     1773           
=======================================
+ Hits        18309    18310    +1     
  Misses        420      420           
  Partials      146      146           

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@mehrdad2m

mehrdad2m commented Jun 1, 2026

Copy link
Copy Markdown
Contributor Author
  • Do we want to use the same attribute for the program entry point (analogous to a main function), as for the quantum kernel entry points?

That's a very good question. Currently we do reuse catalyst.entry_point for both, but they are a slightly different. The semantic of catalyst.entry_point allows for multiple entry points for a module. while something like catalyst.main should be singular in a program. We could have both, but since I am not adding catalyst.entry_point to any other functions (you may argue we should add it for setup and teardown functions as well), we can reuse it as "catalyst.main". but If you prefer having that explicitly, I don't mind adding that as a separate attribute.

  • For the kernels, is there a reason that this is more suitable as a pass rather than an attribute assigned during lowering?

This was not a clear choice for me as I think both are possible. The semantics of catalyst.entry_point, allows for the pass to infer it assuming the kernels entry points are used in the main program. The only downside of adding it from frontend is that every downstream pass that restructures the call graph would have to move the attribute correctly (e.g. if a qnode is cloned and called from a workflow function, you have to remove the attribute from the qnode and attach it to the workflow function) which is a maintenance burden.

Comment thread frontend/catalyst/utils/gen_mlir.py Outdated
Comment thread mlir/lib/Catalyst/Transforms/MarkEntryPoints.cpp
Comment thread frontend/catalyst/utils/gen_mlir.py
mehrdad2m and others added 2 commits June 2, 2026 15:37
Co-authored-by: Mehrdad Malek <39844030+mehrdad2m@users.noreply.github.com>
@dime10

dime10 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

That's a very good question. Currently we do reuse catalyst.entry_point for both, but they are a slightly different. The semantic of catalyst.entry_point allows for multiple entry points for a module. while something like catalyst.main should be singular in a program. We could have both, but since I am not adding catalyst.entry_point to any other functions (you may argue we should add it for setup and teardown functions as well), we can reuse it as "catalyst.main". but If you prefer having that explicitly, I don't mind adding that as a separate attribute.

Hmm, putting the question of having the same attribute for program and kernel entry points aside, why do we actually need multiple entry points for a kernel? I've never seen us make use of it, and even conceptually I would think the kernel should have one entry point.
Although things have a changed a little now that we separated out QNode functions from the kernel concept, because it's actually the QNode functions that need some unique processing at the execution boundary (device setup and measurement processes for instance). I feel like our abstractions aren't as clean as they could be atm 🤔

Coming back to the original question, if we make it so the kernel has exactly one entry point, then I think reusing the attribute is fine. If they do in fact have different semantics separating them out might make sense instead.

@dime10

dime10 commented Jun 5, 2026

Copy link
Copy Markdown
Contributor

The semantics of catalyst.entry_point, allows for the pass to infer it assuming the kernels entry points are used in the main program. The only downside of adding it from frontend is that every downstream pass that restructures the call graph would have to move the attribute correctly (e.g. if a qnode is cloned and called from a workflow function, you have to remove the attribute from the qnode and attach it to the workflow function) which is a maintenance burden.

This is true, there is a bit of maintenance burden, although this applies to all invariants of our IR that passes need to respect. They also need to make sure to propagate, add, and remove the quantum.node attribute as needed as well. I actually had a WIP branch to formalize the entry point attribute similar to qnode attribute, and the idea was to use the verifier to ensure that passes are not violating our invariants.

Relying on this pass alone for example doesn't fix the problem, unless you always run it right before you need it (otherwise passes can still invalidate the attribute invariant after the pass was run). Even then you'd have to make sure the pass deletes any existing ones too, not just adding them for the current launch_kernel ops (as the pass currently does).

Overall I would lean towards an "invariants" approach rather than an "analysis" approach, but I'm curious to hear what you think.

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