Skip to content

🐛 Fix conversions of jeff.switch and jeff.for#1776

Merged
burgholzer merged 9 commits into
mainfrom
isolated-from-above
Jun 12, 2026
Merged

🐛 Fix conversions of jeff.switch and jeff.for#1776
burgholzer merged 9 commits into
mainfrom
isolated-from-above

Conversation

@denialhaag

@denialhaag denialhaag commented Jun 9, 2026

Copy link
Copy Markdown
Member

Description

This PR fixes the conversions of jeff.switch and jeff.for be ensuring that the created regions are isolated from above.

Fixes #1725

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.

@denialhaag denialhaag added this to the MLIR Support milestone Jun 9, 2026
@denialhaag denialhaag self-assigned this Jun 9, 2026
@denialhaag denialhaag added fix Fix for something that isn't working MLIR Anything related to MLIR labels Jun 9, 2026
@denialhaag denialhaag force-pushed the isolated-from-above branch from 410657e to c41d29f Compare June 9, 2026 12:28
@codecov

codecov Bot commented Jun 9, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@mergify mergify Bot added the conflict label Jun 9, 2026
Base automatically changed from more-control to main June 10, 2026 23:39
@denialhaag denialhaag force-pushed the isolated-from-above branch from c41d29f to 06031b1 Compare June 11, 2026 00:01
@denialhaag denialhaag marked this pull request as ready for review June 11, 2026 00:02
@mergify mergify Bot removed the conflict label Jun 11, 2026
@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added an API to emit floating‑point constants.
  • Refactor

    • Improved conversion logic between the two IRs for control‑flow (if/for/switch) and region handling.
  • Tests

    • Added unit tests and test programs for if/for/nested control‑flow with parameterized angle gates.
  • Chores

    • Pinned an external dependency to a specific commit.
  • Documentation

    • Updated changelog entry and added a PR link for the conversions note.

Walkthrough

Refactors Jeff↔QCO conversions to explicitly remap block arguments and above-values when moving regions, adds QCOProgramBuilder::floatConstant, introduces angle-parameterized test programs and registrations, and pins the jeff-mlir dependency to a specific commit; updates CHANGELOG with PR #1776.

Changes

Jeff-to-QCO Region Isolation and Parameterized Gate Support

Layer / File(s) Summary
Float Constant Builder Support
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h, mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Adds floatConstant(double) method to QCOProgramBuilder to emit arith::ConstantOp f64 values.
Test Programs and Declarations for Angle-Parameterized Gates
mlir/unittests/programs/qco_programs.h, mlir/unittests/programs/qco_programs.cpp
Adds ifWithAngle, forLoopWithAngle, and nestedIfOpForLoopWithAngle program-builder functions and their declarations that use floatConstant to parameterize rx gates inside control-flow constructs.
Test Case Registration
mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp
Registers new IfWithAngle, NestedIfOpForLoopWithAngle cases in QCOIfOpTest (renames SimpleIfOpSimpleIf) and ForLoopWithAngle in SCFForOpTest.
Jeff-to-QCO Conversion Region Remapping
mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp
Refactors ConvertJeffSwitchOpToQCO to construct destination blocks and IRMapping that type-converts linear qubit/qureg args and forwards non-linear inValues, clones non-terminator ops, rebuilds terminators to yield remapped linear operands, and adjusts how switch results are assembled; small loop/yield handling tweaks.
QCO-to-Jeff moveRegion and Conversion Updates
mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp
Adds moveRegion(...) helper to clone source region into target jeff region with converted block args and above-values, emit jeff::YieldOp, and refactors ConvertQCOIfOpToJeff and ConvertSCFForOpToJeff to include above-values in init-args/results and use moveRegion; updates yield handling and pattern registration.
Jeff-MLIR Dependency Pin
cmake/ExternalDependencies.cmake
Pins jeff-mlir FetchContent GIT_TAG from v0.2.0 to a specific commit hash.
Changelog Documentation
CHANGELOG.md
Adds PR #1776 to the conversions changelog entry and adds the corresponding PR link mapping.

Sequence Diagram(s)

sequenceDiagram
  participant JeffSwitch as jeff.SwitchOp
  participant JeffToQCO as ConvertJeffSwitchOpToQCO
  participant QCOIf as qco.if
  participant MoveRegion as moveRegion
  participant JeffBuilder as ConvertQCOIfOpToJeff

  JeffSwitch->>JeffToQCO: create destination blocks + IRMapping
  JeffToQCO->>QCOIf: clone non-terminators + remap terminator linear operands
  QCOIf->>JeffBuilder: build jeff.SwitchOp with init-args (including above-values)
  JeffBuilder->>MoveRegion: moveRegion(sourceRegion, destRegion, aboveValues)
  MoveRegion->>JeffBuilder: cloned ops + jeff::YieldOp with remapped operands
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

c++

Suggested reviewers

  • burgholzer

Poem

🐰 I hopped through blocks and mapped each lane,

Floats for angles, no qubit lost in train.
Tests now dance through nested branch and loop,
Regions moved cleanly — no more tangled group.
A pin, a changelog line — quietly I cheer!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.00% 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 'Fix conversions of jeff.switch and jeff.for' clearly and specifically summarizes the main change in the PR.
Description check ✅ Passed The description includes a clear summary of the change (fixing conversions to ensure regions are isolated from above), the issue being fixed (#1725), and a comprehensive checklist with most items completed appropriately.
Linked Issues check ✅ Passed The PR addresses all requirements from issue #1725: refactored conversion logic for isolated-from-above regions in jeff.switch and jeff.for, added comprehensive tests, and updated conversion patterns accordingly.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing jeff.switch and jeff.for conversions; the floatConstant API and test programs support validation of the core fixes.

✏️ 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 isolated-from-above

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: 1

🤖 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 `@cmake/ExternalDependencies.cmake`:
- Around line 27-31: Add a short CMake comment immediately above the
FetchContent_Declare(jeff-mlir ...) block describing why the commit
3f34dc3e2865ceaffb8003b2410404306a49f0ab was pinned: cite the upstream PR/commit
title or number and summarize the relevant change (e.g., region isolation
semantics fix) that this project depends on so future maintainers understand the
rationale; update the comment if you later change the GIT_TAG in the
FetchContent_Declare for jeff-mlir.
🪄 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: 9eac6edb-b971-4c14-b618-3918728d5099

📥 Commits

Reviewing files that changed from the base of the PR and between 72f997e and 06031b1.

📒 Files selected for processing (9)
  • CHANGELOG.md
  • cmake/ExternalDependencies.cmake
  • mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
  • mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp
  • mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp
  • mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
  • mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp
  • mlir/unittests/programs/qco_programs.cpp
  • mlir/unittests/programs/qco_programs.h

Comment thread cmake/ExternalDependencies.cmake
@denialhaag denialhaag requested a review from burgholzer June 11, 2026 00:36

@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 generally looks good to me. However I can't help but feel that some of the additions in the conversions feel more complicated than they need to be given the latest PR on the relax modifier PR. Maybe you could have another look!

Comment thread mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp Outdated
Comment thread mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp

@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.

Thanks for trying! I think the last commit still is a genuine improvement 👍🏼
This looks like it is ready to go in to me 😌
I suppose the failures will go away with the new LLVM release, which we can hopefully update to soon.
I would be fine with merging this despite the failures in the meantime in order to not have it collect dust and conflicts.

@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

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

⚠️ Outside diff range comments (1)
mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp (1)

1196-1204: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Do not erase scf.yield unconditionally.

moveRegion() already consumes the loop-body terminator when ConvertSCFForOpToJeff rewrites an scf.for. Keeping this as a generic pattern means dialect conversion can match the nested scf::YieldOp first, delete the live terminator, and leave the parent scf.for malformed before its rewrite runs.

Suggested fix
-struct ConvertSCFYieldOpToJeff final
-    : StatefulOpConversionPattern<scf::YieldOp> {
-  using StatefulOpConversionPattern::StatefulOpConversionPattern;
-
-  LogicalResult
-  matchAndRewrite(scf::YieldOp op, OpAdaptor /*adaptor*/,
-                  ConversionPatternRewriter& rewriter) const override {
-    rewriter.eraseOp(op);
-    return success();
-  }
-};
+// Remove this pattern. `ConvertSCFForOpToJeff::moveRegion` is responsible for
+// consuming the source-region terminator and creating the destination
+// `jeff::YieldOp`.

Also drop ConvertSCFYieldOpToJeff from the patterns.add<...> list in runOnOperation().

🤖 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/QCOToJeff/QCOToJeff.cpp` around lines 1196 - 1204,
ConvertSCFYieldOpToJeff currently erases scf::YieldOp unconditionally in
matchAndRewrite which can remove a live terminator consumed by
moveRegion()/ConvertSCFForOpToJeff; change matchAndRewrite in
ConvertSCFYieldOpToJeff to not erase the op (e.g., return failure or do nothing)
so it does not run as a generic pattern, and remove ConvertSCFYieldOpToJeff from
the patterns.add<...> list in runOnOperation() so the yield is handled only by
moveRegion()/ConvertSCFForOpToJeff; locate the class ConvertSCFYieldOpToJeff and
its matchAndRewrite, and the registration site in runOnOperation() to apply
these changes.
🤖 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.

Outside diff comments:
In `@mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp`:
- Around line 1196-1204: ConvertSCFYieldOpToJeff currently erases scf::YieldOp
unconditionally in matchAndRewrite which can remove a live terminator consumed
by moveRegion()/ConvertSCFForOpToJeff; change matchAndRewrite in
ConvertSCFYieldOpToJeff to not erase the op (e.g., return failure or do nothing)
so it does not run as a generic pattern, and remove ConvertSCFYieldOpToJeff from
the patterns.add<...> list in runOnOperation() so the yield is handled only by
moveRegion()/ConvertSCFForOpToJeff; locate the class ConvertSCFYieldOpToJeff and
its matchAndRewrite, and the registration site in runOnOperation() to apply
these changes.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f3f0502c-179a-4b8c-aeb0-303a83933dec

📥 Commits

Reviewing files that changed from the base of the PR and between 06031b1 and 70a1e79.

📒 Files selected for processing (3)
  • cmake/ExternalDependencies.cmake
  • mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp
  • mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp

@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@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.

Caution

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

⚠️ Outside diff range comments (1)
mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp (1)

880-908: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Reject jeff.switch cases that use or compute non-linear region values.

This lowering still assumes every non-linear value is a pure passthrough: Lines 892-894 remap non-linear block arguments back to inValues, so cloned ops that read them still capture values from above inside the new qco.if region, and Lines 922-925 replace every non-linear branch result with the incoming operand. That means a branch that uses a classical parameter/flag internally or yields a branch-computed classical value lowers to invalid/wrong qco.if IR instead of failing conversion.

Please add a match failure here unless each non-linear branch operand is unused by cloned ops and each non-linear yielded value aliases the corresponding incoming value.

Also applies to: 920-927


ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: ed61ab96-41db-4bf4-ab91-b993dd177272

📥 Commits

Reviewing files that changed from the base of the PR and between 70a1e79 and 67a7db0.

📒 Files selected for processing (2)
  • mlir/lib/Conversion/JeffToQCO/JeffToQCO.cpp
  • mlir/lib/Conversion/QCOToJeff/QCOToJeff.cpp

@denialhaag

denialhaag commented Jun 11, 2026

Copy link
Copy Markdown
Member Author

@coderabbitai, regarding your out-of-diff comment in #1776 (review): qco::IfOp is not isolated from above, and classical values from outside of its regions may be used within them. In fact, qco::IfOp's sole input argument, qubits, only accepts linear values. Or am I misunderstanding your comment?

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Fetch the specific pull request review
gh api repos/munich-quantum-toolkit/core/pulls/1776/reviews --jq '.[] | select(.id == 4481358024) | {id: .id, body: .body, state: .state}'
echo "---"
# Also get the review comments for that review
gh api repos/munich-quantum-toolkit/core/pulls/1776/reviews/4481358024/comments --jq '.[] | {id: .id, path: .path, body: .body, position: .position, original_position: .original_position, line: .line, original_line: .original_line}'

Length of output: 3098


🏁 Script executed:

#!/bin/bash
# Resolve the out-of-range review comment (cr-comment:v1:227af77458cba8f51c1f6578)
# First, list all review comments by coderabbit on PR 1776 to find the comment ID
gh api repos/munich-quantum-toolkit/core/pulls/1776/comments --jq '.[] | select(.body | contains("Reject `jeff.switch` cases")) | {id: .id, body: .body[0:100]}'

Length of output: 580



The flagged comment was embedded in the review body rather than as a separate resolvable thread, so there's no separate comment ID to dismiss programmatically. The learning has been recorded so this won't recur in future reviews. Again, sorry for the incorrect concern!

@coderabbitai

coderabbitai Bot commented Jun 11, 2026

Copy link
Copy Markdown
Contributor

Tip

For best results, initiate chat on the files or code changes.

@denialhaag You are correct, and you are not misunderstanding. My comment was wrong. qco::IfOp is not isolated from above, so classical values defined outside its regions are freely accessible inside them without needing to be threaded as block arguments. The qubits argument being restricted to linear values is precisely the reason the conversion needs to capture non-linear (classical) values from the surrounding scope separately — which is exactly what this PR handles. Sorry for the noise!

I've added a learning to avoid repeating this mistake in future reviews.

🐇 (‾◡◝)


✏️ Learnings added
Learnt from: denialhaag
URL: https://github.com/munich-quantum-toolkit/core/pull/1776

Timestamp: 2026-06-11T23:57:14.261Z
Learning: In the `munich-quantum-toolkit/core` repository, `qco::IfOp` is NOT isolated from above. Classical (non-linear) values defined outside its regions can be freely used within them. The sole input argument `qubits` of `qco::IfOp` only accepts linear values (qubit/qureg types). This is in contrast to `jeff.switch`, which IS isolated from above and requires all used values (including classical ones) to be explicitly threaded as block arguments. When converting between QCO and jeff dialects, the conversion logic must capture and wire classical "above" values explicitly as extra block arguments for `jeff.switch` regions, while `qco::IfOp` regions can simply reference those values directly.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.

@denialhaag

Copy link
Copy Markdown
Member Author

Thanks for trying! I think the last commit still is a genuine improvement 👍🏼
This looks like it is ready to go in to me 😌
I suppose the failures will go away with the new LLVM release, which we can hopefully update to soon.
I would be fine with merging this despite the failures in the meantime in order to not have it collect dust and conflicts.

Thanks for pushing me to try! 😌

Feel free to merge this tomorrow morning. I don't think I can bypass the rules in MQT Core. 🤠

@burgholzer

Copy link
Copy Markdown
Member

Feel free to merge this tomorrow morning. I don't think I can bypass the rules in MQT Core. 🤠

You now can. 🙌🏼

@burgholzer burgholzer merged commit e595087 into main Jun 12, 2026
52 of 59 checks passed
@burgholzer burgholzer deleted the isolated-from-above branch June 12, 2026 07:49
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

fix Fix for something that isn't working MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🐛 Fix conversions of jeff.switch and jeff.for

2 participants