Skip to content
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

Fix issues in lift-array-alloc #2570

Open
wants to merge 2 commits into
base: main
Choose a base branch
from

Conversation

annagrin
Copy link
Collaborator

@annagrin annagrin commented Jan 31, 2025

Description

Fix crashes and over-optimizations in lift-array-alloc:

Example:

  qubits = cudaq.qvector(2)
  arr1 = [1]
  t = arr1[0]
  arr2 = [0]
  arr2[0] = t
  b = arr2[0]
  x(qubits[b])

This example generates the following IR:

func.func @test3() {
  %c0_i64 = arith.constant 0 : i64
  %c1_i64 = arith.constant 1 : i64

  // qubits = cudaq.qvector(2)
  %0 = quake.alloca !quake.veq<2>

  // arr1 = [1]
  %1 = cc.alloca !cc.array<i64 x 1>
  %2 = cc.cast %1 : (!cc.ptr<!cc.array<i64 x 1>>) -> !cc.ptr<i64>
  cc.store %c1_i64, %2 : !cc.ptr<i64>

  // t = arr1[0]
  %3 = cc.load %2 : !cc.ptr<i64>

  // arr2 = [0]
  %4 = cc.alloca !cc.array<i64 x 1>
  %5 = cc.cast %4 : (!cc.ptr<!cc.array<i64 x 1>>) -> !cc.ptr<i64>
  cc.store %c0_i64, %5 : !cc.ptr<i64> // Dominates the next store, don't lift

  // arr2[0] = t
  cc.store %3, %5 : !cc.ptr<i64>

  // b = arr2[0]
  %6 = cc.load %5 : !cc.ptr<i64>

  // x(qubits[b])
  %7 = quake.extract_ref %0[%6] : (!quake.veq<2>, i64) -> !quake.ref
  quake.x %7 : (!quake.ref) -> ()
  return
}

And lift-array-alloc misses the second store to arr2 below and incorrectly assumes that arr2[0]==0 instead of arr2[0]==1:

  cc.store %c0_i64, %5 : !cc.ptr<i64> // Dominates the next store, don't lift
  cc.store %3, %5 : !cc.ptr<i64>

The fix detects that the first store dominates the second and skips the optimization.

@annagrin annagrin requested a review from schweitzpgi January 31, 2025 19:53
I, Anna Gringauze <[email protected]>, hereby add my Signed-off-by to this commit: c1592b8

Signed-off-by: Anna Gringauze <[email protected]>
@annagrin annagrin changed the title Fix issues in lif-array-alloc Fix issues in lift-array-alloc Jan 31, 2025
ArrayRef<cudaq::cc::ExtractValueArg>{offset});
rewriter.replaceAllUsesWith(load, extractValue);
insertOpToErase(load);
Copy link
Collaborator

Choose a reason for hiding this comment

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

I don't see why the replaceOpWithNewOp needs to be changed here. It will

  • create the new ExtractValueOp
  • replace all the users of the original LoadOp to use the new ExtractValueOp

which is exactly what your change does. Additionally, we don't need or want to aggressively delete Ops. For example, passes may not be successful, and the MLIR infrastructure is designed to allow it to cleanly back out of the changes without leaving the IR broken. We should just let MLIR do the cleanups anywhere that it can. In this case, a LoadOp with 0 users will simply be removed by the next DCE. We don't have to do anything special.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Not sure why but the original code crashes on uccsd example

@@ -160,6 +167,7 @@ class AllocaPattern : public OpRewritePattern<cudaq::cc::AllocaOp> {
if (!u)
return nullptr;
if (auto store = dyn_cast<cudaq::cc::StoreOp>(u)) {
storeSets[index].insert(u);
if (op.getOperation() == store.getPtrvalue().getDefiningOp() &&
isa_and_present<arith::ConstantOp, complex::ConstantOp>(
store.getValue().getDefiningOp())) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

How does any of this get past the check here? This is intended to check that we do not have multiple stores to the same offset of the array.

Copy link
Collaborator

Choose a reason for hiding this comment

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

I ran a little test, and this check correctly blocks the transformation.

func.func @f() {
    %0 = cc.alloca !cc.array<i64 x 1>
    %1 = arith.constant 1 : i64
    %2 = cc.compute_ptr %0[0] : (!cc.ptr<!cc.array<i64 x 1>>) -> !cc.ptr<i64>
    cc.store %1, %2 : !cc.ptr<i64>
    %3 = arith.constant 3 : i64
    cc.store %3, %2 : !cc.ptr<i64>
    return
}

Running lift-array-alloc does not change this code, which is the correct thing to happen.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think the failing example differs on the second store not using a constant to store to a pointer

Comment on lines +147 to +150
cc.store %c0_i64, %5 : !cc.ptr<i64> // Dominates the next store, don't lift

// arr2[0] = t
cc.store %3, %5 : !cc.ptr<i64>
Copy link
Collaborator

@schweitzpgi schweitzpgi Jan 31, 2025

Choose a reason for hiding this comment

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

The pass should refuse to convert this code because there are 2 stores to the same offset. But I don't see why it wasn't already. But this is a good test to make sure it doesn't regress further.

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

second store is not a constant. I will update the fix

Copy link

CUDA Quantum Docs Bot: A preview of the documentation can be found here.

github-actions bot pushed a commit that referenced this pull request Jan 31, 2025
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.

2 participants