Skip to content

Fuse matching product scf.if regions - #484

Open
1sgtpepper wants to merge 92 commits into
project-llzk:mainfrom
1sgtpepper:fuse-product-if-regions
Open

1sgtpepper wants to merge 92 commits into
project-llzk:mainfrom
1sgtpepper:fuse-product-if-regions

Conversation

@1sgtpepper

@1sgtpepper 1sgtpepper commented May 26, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Fuse matching compute/constrain scf.if regions in product functions while preserving constrained signal reads, source order, and observable effects. Unsafe or ambiguous pairs remain separate.

Related issues

Part of #300. Supports identical SSA conditions and resultless constrain conditionals.

Changes

  • Preserve compute results, compatible conditional/yield attributes, and combined source locations during conditional fusion.
  • Retain eligible constrain-side signal reads and reject movement across observable effects or invalid dominance.
  • Keep loop fusion restricted to matching induction sequences and comparison modes with safe body interleaving. Document the loop and conditional legality rules separately.

Testing

  • Fork validation passed: nix build for .#debugGCC, .#debugClangCov, .#checks.x86_64-linux.llzk-installcheck-debug, and .#checks.x86_64-linux.llzk-installcheck-release; nix --option sandbox false --print-build-logs build .#docs.
  • Fixture replay: test commands passed; output-comparison differences were reviewed manually (replay job remains failed).
  • Style checks: git-clang-format 20.1.8 passed; clang-tidy reported no new LLZK source diagnostics.

Submission checklist

  • If I am an external contributor, this PR has a linked issue marked approved; otherwise, this does not apply.
  • I added or updated tests for all relevant behavior, or explained above why tests are not needed.
  • I updated the relevant TableGen or other documentation, or explained above why documentation is not needed.
  • I added a changelog entry describing user-visible changes. (create-changelog in the Nix development shell creates a template.)
  • I enabled Allow edits from maintainers if this PR comes from a fork.

AI assistance

  • No AI tools contributed to this PR.
  • AI tools contributed to this PR.

Tools used: AI coding assistant

How the tools contributed: Implementation and code review.

How I verified the contribution: Reviewed the final diff and exact-head hosted results.

@iangneal
iangneal requested a review from a team May 28, 2026 01:26
@iangneal

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: b4213c67d7

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated

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

The constrain use is remapped to the branch-local compute value instead of hoisting the read.

I may be misunderstanding, but I think this is not safe when the struct.readm is a constrain op:

%0 = felt.add %a, %b {source = "compute"}
struct.writem %self[@foo] = %0 {source = "compute"}
%foo = struct.readm %self[@foo] {source = "constrain"}
constrain.eq %foo, %c {source = "constrain"}

emits a constraint guaranteeing that the @foo signal equals the result of %c, whereas in the remapped

%0 = felt.add %a, %b {source = "compute"}
struct.writem %self[@foo] = %0 {source = "compute"}
constrain.eq %0, %c {source = "constrain"}

the emitted constraint doesn't refer to a struct signal at all.

@1sgtpepper

Copy link
Copy Markdown
Contributor Author

Thanks for catching this, Raghav. You're right. Remapping the struct.readm result to the branch local compute value can remove the member signal from the emitted constraint. I'll make this fusion case conservative for now and add a regression test so we keep the constraint tied to the member read.

Copilot AI 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.

Pull request overview

This PR extends the -llzk-fuse-product-loops pass to also fuse matching compute/constrain scf.if regions in struct product programs (Fixes #300), and adds regression tests covering fusable and non-fusable scf.if patterns.

Changes:

  • Add scf.if fusion logic that clones compute + constrain branches into a single fused scf.if when conditions and structural constraints match.
  • Refactor pass entry/recursion to fuse both scf.if and scf.for control flow via a shared fuseMatchingRegionControlFlow routine.
  • Add lit/FileCheck tests for scf.if fusion and “do not fuse” guardrails; add an unreleased changelog entry.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
lib/Transforms/LLZKFuseProductLoopsPass.cpp Implements scf.if fusion and refactors recursion over region control flow.
test/Transforms/FuseProductLoops/fuse_if_no_member_read.llzk Positive test ensuring scf.if fusion occurs in a safe case.
test/Transforms/FuseProductLoops/fuse_loop_then_if.llzk Ensures loop fusion still works and enables nested scf.if fusion after loop fusion.
test/Transforms/FuseProductLoops/no_fuse_if_call_after_write.llzk Negative test: blocks fusion when a call occurs after a mapped write.
test/Transforms/FuseProductLoops/no_fuse_if_condition_mismatch.llzk Negative test: blocks fusion when conditions differ.
test/Transforms/FuseProductLoops/no_fuse_if_internal_read.llzk Negative test: blocks fusion when constrain side reads members inside the scf.if.
test/Transforms/FuseProductLoops/no_fuse_if_read_after_write.llzk Negative test: blocks fusion when a member read appears between the ifs.
changelogs/unreleased/fuse-product-if-regions.yaml Changelog entry for the new scf.if fusion capability.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
@iangneal

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: 326acc24af

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

@1sgtpepper

Copy link
Copy Markdown
Contributor Author

I seem to have found some loose ends, will update in a bit.

@1sgtpepper

Copy link
Copy Markdown
Contributor Author

Pushed a conservative safety guard for moved constrain branches.

Comment thread CHANGELOG.md Outdated
@iangneal

Copy link
Copy Markdown
Contributor

@codex review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Hooray!

Reviewed commit: 8725011bd2

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment thread include/llzk/Transforms/LLZKTransformationPasses.td Outdated
Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
Comment thread lib/Transforms/LLZKFuseProductControlFlowPass.cpp Outdated
Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
Comment thread lib/Transforms/LLZKFuseProductLoopsPass.cpp Outdated
Comment thread test/Transforms/FuseProductLoops/fuse_loop_then_if.llzk Outdated
@1sgtpepper
1sgtpepper force-pushed the fuse-product-if-regions branch from 53ba923 to 6719d38 Compare July 16, 2026 14:11
@tim-hoffman
tim-hoffman removed the request for review from raghav198 August 4, 2026 18:20
@1sgtpepper
1sgtpepper marked this pull request as draft August 6, 2026 13:51
@1sgtpepper

Copy link
Copy Markdown
Contributor Author

I think I've found some issues with the pr will mark draft for now while I fix them

@1sgtpepper
1sgtpepper force-pushed the fuse-product-if-regions branch 2 times, most recently from bb2fad5 to 583a957 Compare August 7, 2026 14:23
@1sgtpepper

Copy link
Copy Markdown
Contributor Author

@iangneal I kept the legacy name to avoid breaking existing CLI and pipeline references. Should I open a follow-up issue to rename them, or keep it as-is?

@tim-hoffman

Copy link
Copy Markdown
Member

@iangneal I kept the legacy name to avoid breaking existing CLI and pipeline references. Should I open a follow-up issue to rename them, or keep it as-is?

Go ahead and do the requested renaming in this PR.

@1sgtpepper
1sgtpepper force-pushed the fuse-product-if-regions branch 2 times, most recently from 10554fc to d3f1aca Compare August 12, 2026 01:42
@1sgtpepper
1sgtpepper marked this pull request as ready for review August 28, 2026 14:00
@1sgtpepper
1sgtpepper marked this pull request as draft August 30, 2026 15:03
@1sgtpepper

Copy link
Copy Markdown
Contributor Author

@tim-hoffman Small non-blocking missed-optimization follow-up related to #619: a same-parent loop in another block can suppress valid same-block fusion. Reproducer · IR output. Result: baseline = 1 fused loop; cross-block = 1 compute + 2 constrain, 0 fused. Should this be tracked under #619?

@1sgtpepper
1sgtpepper marked this pull request as ready for review September 6, 2026 08:38
@1sgtpepper
1sgtpepper marked this pull request as draft September 7, 2026 01:42
@1sgtpepper
1sgtpepper marked this pull request as ready for review September 7, 2026 02:09

This branch has not been deployed

No deployments
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