Skip to content

✨ Add QIRSetAttributesAndMetadata Pass#1787

Open
MatthiasReumann wants to merge 35 commits into
mainfrom
feat/set-qir-metadata-pass
Open

✨ Add QIRSetAttributesAndMetadata Pass#1787
MatthiasReumann wants to merge 35 commits into
mainfrom
feat/set-qir-metadata-pass

Conversation

@MatthiasReumann

@MatthiasReumann MatthiasReumann commented Jun 16, 2026

Copy link
Copy Markdown
Collaborator

Changes

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

@codecov

codecov Bot commented Jun 16, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 90.45643% with 23 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp 93.2% 11 Missing ⚠️
mlir/lib/Support/IRVerification.cpp 70.2% 11 Missing ⚠️
mlir/lib/Compiler/CompilerPipeline.cpp 87.5% 1 Missing ⚠️

📢 Thoughts on this report? Let us know!

@MatthiasReumann MatthiasReumann changed the title ✨ Add AttachEntryPointAttributes Pass ✨ Add AttachQIRAttributes Pass Jun 16, 2026
@MatthiasReumann MatthiasReumann changed the title ✨ Add AttachQIRAttributes Pass ✨ Add QIRSetAttributesAndMetadata Pass Jun 17, 2026
@MatthiasReumann

Copy link
Copy Markdown
Collaborator Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 17, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

The QIRMetadata struct and setQIRAttributes function are removed and replaced by a new QIRSetAttributesAndMetadata MLIR pass that post-hoc analyzes lowered LLVM IR to compute qubit/result counts, dynamic capability flags, and backwards-branching classification, then attaches QIR 2.1 profile attributes and module flags. QIRProgramBuilder and the QC-to-QIR conversion passes are updated to no longer track these flags during lowering.

Changes

QIR Metadata Refactor

Layer / File(s) Summary
Removed QIRMetadata struct, new pass declaration, and API contract changes
mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h, mlir/include/mlir/Dialect/QIR/Transforms/Passes.td, mlir/include/mlir/Support/Passes.h, mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h, mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h, mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h
Deletes QIRMetadata header; removes it as a base class of LoweringState and as the metadata_ member of QIRProgramBuilder; replaces it with plain numQubits/numResults counters. Removes setQIRAttributes declaration. Adds QIRSetAttributesAndMetadata TableGen pass with use-adaptive option. Extends runWithPassManager, populateQIRCleanupPipeline, and runQIRCleanupPipeline to accept useAdaptive.
New QIRSetAttributesAndMetadata pass implementation
mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp
Adds a 405-line pass that computes qubit/result counts by tracing integer constants through inttoptr chains, detects dynamic allocation via callee name matching, classifies backward-branching via CFG back-edge + DominanceInfo analysis, selects base vs. adaptive profile, then removes existing LLVM::ModuleFlagsOps and writes fresh passthrough attributes and module flags.
QIRProgramBuilder refactored to pass-based finalization
mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp
Removes metadata_ flag mutations (useDynamicQubit, backwardsBranching, etc.) throughout staticQubit, staticResult, scfFor, scfWhile, and measure. Replaces direct setQIRAttributes in finalize() with runWithPassManager + createQIRSetAttributesAndMetadata({isAdaptive}).
QC-to-QIR conversion passes: remove metadata flag tracking
mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp, mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp, mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp, mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp
Removes all state.useDynamicQubit, state.useArrays, state.useDynamicResult, state.numResults, state.useAdaptive assignments and the setSCFFlags helper from conversion patterns. Removes setQIRAttributes pipeline stage from both converters. Static qubit/result indexing now derives from container sizes. Deletes setQIRAttributes implementation.
Pipeline API wiring and compiler integration
mlir/lib/Support/Passes.cpp, mlir/lib/Compiler/CompilerPipeline.cpp, mlir/lib/Compiler/CMakeLists.txt
Makes runWithPassManager externally visible. Inserts createQIRSetAttributesAndMetadata({useAdaptive}) into populateQIRCleanupPipeline. Updates CompilerPipeline to inline adaptive/base pass selection and forward config_.convertToQIRAdaptive to the cleanup pipeline.
IR verification and test updates
mlir/lib/Support/IRVerification.cpp, mlir/unittests/.../*, CHANGELOG.md
Extends compareAttributes to handle ArrayAttr, FlatSymbolRefAttr, and LLVM attribute types (TailCallKindAttr, FastmathFlagsAttr, CConvAttr, ModuleFlagAttr). Updates all ProgramEquivalence tests to pass the useAdaptive boolean to runQIRCleanupPipeline.

Sequence Diagram(s)

sequenceDiagram
  participant CompilerPipeline
  participant QCToQIRAdaptive_Base as QCToQIRAdaptive / QCToQIRBase
  participant runQIRCleanupPipeline
  participant populateQIRCleanupPipeline
  participant QIRSetAttributesAndMetadata

  CompilerPipeline->>QCToQIRAdaptive_Base: runStage: add adaptive or base pass (no metadata flags set)
  CompilerPipeline->>runQIRCleanupPipeline: runQIRCleanupPipeline(module, useAdaptive)
  runQIRCleanupPipeline->>populateQIRCleanupPipeline: populateQIRCleanupPipeline(pm, useAdaptive)
  populateQIRCleanupPipeline->>QIRSetAttributesAndMetadata: createQIRSetAttributesAndMetadata({useAdaptive})
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: getNumQubits() trace constants→inttoptr→QIR calls
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: getNumResults() trace QIR_RECORD_OUTPUT
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: usesBackwardsBranching() DominanceInfo + CondBrOp
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: usesDynamic() callee name matching
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: removeExistingModuleFlags()
  QIRSetAttributesAndMetadata->>QIRSetAttributesAndMetadata: setMetadata(): write passthrough + ModuleFlagsOp
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • munich-quantum-toolkit/core#1264: Introduced the original QIR attribute/metadata infrastructure (QIRMetadata, setQIRAttributes) that this PR fully replaces.
  • munich-quantum-toolkit/core#1580: Modified the same QIR builder/utils/cleanup pipeline paths that this PR refactors, including register integration with QIR metadata tracking.
  • munich-quantum-toolkit/core#1624: Modified QIRProgramBuilder's qubit allocation path and dynamic-qubit tracking, which this PR replaces with the new numQubits/numResults counter approach.

Suggested reviewers

  • burgholzer

Poem

🐇 Hop hop, the metadata's gone astray,
No more duplicated flags in the fray!
A shiny new pass now walks the IR,
Counting qubits and results near and far. ✨
The cleanup pipeline runs true and bright—
QIR 2.1 profiles set just right! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 38.30% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: adding a new QIRSetAttributesAndMetadata pass, which is the core objective of this PR.
Linked Issues check ✅ Passed The PR addresses issue #1777 by implementing a QIRSetAttributesAndMetadata pass that prevents duplicate metadata attributes. The pass is integrated into qirbuilder.finalize() and the cleanup pipeline to ensure metadata is set correctly once.
Out of Scope Changes check ✅ Passed All changes are directly related to the objective of fixing duplicated metadata and implementing the new pass. Updates to test files, pass definitions, IR verification, and cleanup pipelines are all in scope.
Description check ✅ Passed The pull request description includes a summary of changes, references the resolved issue, and provides a completed checklist with all items marked as done, though without detailed elaboration on specific changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch feat/set-qir-metadata-pass

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp (1)

475-483: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Documentation lists removed stage.

The class-level docstring still references "6. Set QIR metadata attributes" which has been removed from the implementation. The stage numbers in the docstring (1-8) don't match the actual implementation stages (1-7).

📝 Suggested fix
 * Conversion stages:
 * 1. Convert scf dialect to cf
-* 2. Cpmvert func dialect to LLVM
+* 2. Convert func dialect to LLVM
 * 3. Ensure proper block structure for QIR Adaptive Profile
 * 4. Add QIR initialization call
 * 5. Convert QC and memref operations to QIR calls
-* 6. Set QIR metadata attributes
-* 7. Convert arith and cf dialects to LLVM
-* 8. Reconcile unrealized casts
+* 6. Convert arith and cf dialects to LLVM
+* 7. Reconcile unrealized casts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp` around lines 475
- 483, The docstring in the QCToQIRAdaptive.cpp file describes eight conversion
stages but the implementation only contains seven stages. Update the comment
block by removing the line "6. Set QIR metadata attributes" and renumbering the
subsequent stage from "7. Convert arith and cf dialects to LLVM" to "6. Convert
arith and cf dialects to LLVM" and "8. Reconcile unrealized casts" to "7.
Reconcile unrealized casts" to ensure the documented stages match the actual
implementation.
mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp (1)

288-296: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Class-level docstring still lists removed metadata stage.

The class documentation lists "5. Set QIR metadata attributes" which has been removed from the implementation. Update the stage list to match the actual 6-stage implementation.

📝 Suggested fix
 * Conversion stages:
 * 1. Convert func dialect to LLVM
 * 2. Ensure proper block structure for QIR base profile
 * 3. Add QIR initialization call
 * 4. Convert QC operations to QIR calls
-* 5. Set QIR metadata attributes
-* 6. Convert arith and cf dialects to LLVM
-* 7. Reconcile unrealized casts
+* 5. Convert arith and cf dialects to LLVM
+* 6. Reconcile unrealized casts
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp` around lines 288 - 296,
The class-level docstring in the conversion function lists a seven-stage
conversion process including "5. Set QIR metadata attributes", but this stage
has been removed from the actual implementation. Update the docstring to remove
stage 5 and renumber the remaining stages so that "Convert arith and cf dialects
to LLVM" becomes stage 5 and "Reconcile unrealized casts" becomes stage 6,
making the documented process match the actual six-stage implementation.
mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp (1)

103-114: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick win

Add explicit assertions for QIR metadata deduplication.

ProgramEquivalence cleans both program and reference with runQIRCleanupPipeline(..., true). A symmetric regression in cleanup (e.g., duplicated required_num_qubits / required_num_results on both sides) can still pass equivalence. Add direct post-cleanup assertions that these keys occur exactly once in entry-point passthrough metadata.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp`
around lines 103 - 114, The test currently relies on ProgramEquivalence to
verify both programs match, but symmetric regressions (where duplicated metadata
appears on both sides) would still pass equivalence checks. After calling
runQIRCleanupPipeline on both program and reference (which occurs after line 103
and before line 114), add explicit assertions to verify that metadata
deduplication worked correctly. Specifically, add assertions to check that
metadata keys like required_num_qubits and required_num_results appear exactly
once in the entry-point passthrough metadata for both the program and reference
objects to catch any symmetric metadata duplication issues.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp`:
- Around line 65-70: The runOnOperation method calls getMainFunction which can
return nullptr when no function with the entry_point attribute exists, but does
not check for null before passing the result to setMetadata, getAdaptive, or
getBase. Add a null check on the main variable immediately after the
getMainFunction call and return early from runOnOperation if main is nullptr to
prevent null pointer dereference.
- Around line 374-375: Fix the Doxygen comment for the getAdaptive method which
currently incorrectly states "QIR base profile compliant program" but should
state "QIR adaptive profile compliant program" since the method name and
functionality are related to the adaptive profile, not the base profile. Update
the comment text to accurately reflect that this method returns metadata for the
adaptive profile.
- Around line 247-255: In the classifyCondBrOp function, the call to
condition.getDefiningOp() can return nullptr when the condition is a block
argument rather than an operation result, and passing nullptr to dyn_cast will
cause a crash. Add a null check on the result of getDefiningOp() before
attempting the dyn_cast to LLVM::CallOp, and if it is null, handle that case
appropriately (the existing comment suggests returning true for non-measurement
conditions).

---

Outside diff comments:
In `@mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp`:
- Around line 475-483: The docstring in the QCToQIRAdaptive.cpp file describes
eight conversion stages but the implementation only contains seven stages.
Update the comment block by removing the line "6. Set QIR metadata attributes"
and renumbering the subsequent stage from "7. Convert arith and cf dialects to
LLVM" to "6. Convert arith and cf dialects to LLVM" and "8. Reconcile unrealized
casts" to "7. Reconcile unrealized casts" to ensure the documented stages match
the actual implementation.

In `@mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp`:
- Around line 288-296: The class-level docstring in the conversion function
lists a seven-stage conversion process including "5. Set QIR metadata
attributes", but this stage has been removed from the actual implementation.
Update the docstring to remove stage 5 and renumber the remaining stages so that
"Convert arith and cf dialects to LLVM" becomes stage 5 and "Reconcile
unrealized casts" becomes stage 6, making the documented process match the
actual six-stage implementation.

In
`@mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp`:
- Around line 103-114: The test currently relies on ProgramEquivalence to verify
both programs match, but symmetric regressions (where duplicated metadata
appears on both sides) would still pass equivalence checks. After calling
runQIRCleanupPipeline on both program and reference (which occurs after line 103
and before line 114), add explicit assertions to verify that metadata
deduplication worked correctly. Specifically, add assertions to check that
metadata keys like required_num_qubits and required_num_results appear exactly
once in the entry-point passthrough metadata for both the program and reference
objects to catch any symmetric metadata duplication issues.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: bbb22f46-55eb-405d-af7d-6e976a1662b3

📥 Commits

Reviewing files that changed from the base of the PR and between 12cf546 and 026ba2c.

📒 Files selected for processing (21)
  • CHANGELOG.md
  • mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h
  • mlir/include/mlir/Dialect/QIR/Builder/QIRProgramBuilder.h
  • mlir/include/mlir/Dialect/QIR/Transforms/Passes.td
  • mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h
  • mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h
  • mlir/include/mlir/Support/Passes.h
  • mlir/lib/Compiler/CMakeLists.txt
  • mlir/lib/Compiler/CompilerPipeline.cpp
  • mlir/lib/Conversion/QCToQIR/QIRAdaptive/QCToQIRAdaptive.cpp
  • mlir/lib/Conversion/QCToQIR/QIRBase/QCToQIRBase.cpp
  • mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp
  • mlir/lib/Dialect/QIR/Builder/QIRProgramBuilder.cpp
  • mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp
  • mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp
  • mlir/lib/Support/IRVerification.cpp
  • mlir/lib/Support/Passes.cpp
  • mlir/unittests/Compiler/test_compiler_pipeline.cpp
  • mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp
  • mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp
  • mlir/unittests/Dialect/QIR/IR/test_qir_ir.cpp
💤 Files with no reviewable changes (4)
  • mlir/include/mlir/Dialect/QIR/Utils/QIRMetadata.h
  • mlir/include/mlir/Dialect/QIR/Utils/QIRUtils.h
  • mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp
  • mlir/lib/Dialect/QIR/Utils/QIRUtils.cpp

Comment thread mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp
Comment thread mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp Outdated
Comment thread mlir/lib/Dialect/QIR/Transforms/AttachQIRAttributes.cpp Outdated
@mergify mergify Bot added the conflict label Jun 17, 2026
@MatthiasReumann MatthiasReumann self-assigned this Jun 17, 2026
@MatthiasReumann MatthiasReumann added this to the MLIR Support milestone Jun 17, 2026
@mergify mergify Bot removed the conflict label Jun 17, 2026
@MatthiasReumann MatthiasReumann marked this pull request as ready for review June 17, 2026 12:32

@burgholzer burgholzer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This looks very clean! Nice work.
I just have one minor question and a typo left here.
Otherwise, this is good to go!

Comment on lines +138 to +147
/// Remove existing module flag operations from module.
/// Note that this might also erase non-QIR module flag operations, but for
/// now, we assume that there are no others.
static void removeExistingModuleFlags(ModuleOp m, IRRewriter& rewriter) {
SmallVector<Operation*> flagOps;
m->walk([&](LLVM::ModuleFlagsOp op) { flagOps.emplace_back(op); });
for (Operation* op : llvm::make_early_inc_range(flagOps)) {
rewriter.eraseOp(op);
}
}

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Does this have to be a two-step process? Isn't there some kind of functionality that would simply erase all LLVM::ModuleFlagsOp?

return seen.size();
}

/// Determine whether an loop (as a set of blocks) is an iterative loop (true)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Suggested change
/// Determine whether an loop (as a set of blocks) is an iterative loop (true)
/// Determine whether a loop (as a set of blocks) is an iterative loop (true)

@burgholzer burgholzer added usability Anything related to usability MLIR Anything related to MLIR labels Jun 17, 2026
@mergify mergify Bot added the conflict label Jun 17, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

conflict MLIR Anything related to MLIR usability Anything related to usability

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Invalid / Duplicated Metadata Produced By QIR Builder / Translation

2 participants