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

Add WriteAfterWriteElimination pass #2572

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

Conversation

annagrin
Copy link
Collaborator

@annagrin annagrin commented Feb 1, 2025

Description

Add a pass for removing stores that are overridden by other stores

Example:

func.func @test_two_stores_same_pointer() {
  %c0_i64 = arith.constant 0 : i64
  %0 = quake.alloca !quake.veq<2>
  %1 = cc.const_array [1] : !cc.array<i64 x 1>
  %2 = cc.extract_value %1[0] : (!cc.array<i64 x 1>) -> i64
  %3 = cc.alloca !cc.array<i64 x 1>
  %4 = cc.cast %3 : (!cc.ptr<!cc.array<i64 x 1>>) -> !cc.ptr<i64>
  cc.store %c0_i64, %4 : !cc.ptr<i64>
  cc.store %2, %4 : !cc.ptr<i64>
  %5 = cc.load %4 : !cc.ptr<i64>
  %6 = quake.extract_ref %0[%5] : (!quake.veq<2>, i64) -> !quake.ref
  quake.x %6 : (!quake.ref) -> ()
  return
}

The in the last two stores, the first one can be removed because it is overridden by the next.

  cc.store %c0_i64, %4 : !cc.ptr<i64>
  cc.store %2, %4 : !cc.ptr<i64>

Signed-off-by: Anna Gringauze <agringauze@nvidia.com>
Copy link

github-actions bot commented Feb 1, 2025

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

github-actions bot pushed a commit that referenced this pull request Feb 1, 2025
Copy link

@iqbalbhatti49 iqbalbhatti49 left a comment

Choose a reason for hiding this comment

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

Review Summary:

The code looks great overall! You've followed best practices, and the implementation is clean and efficient. The logic is sound, and I appreciate the attention to detail in the structure.

Highlights:

  • Code is well-organized and easy to follow.
  • Proper handling of edge cases.
  • Clear variable naming makes the code readable.

Overall, I approve this change and recommend merging it. Great work!

@annagrin annagrin requested a review from schweitzpgi February 2, 2025 01:23
…eless-stores

Signed-off-by: Anna Gringauze <agringauze@nvidia.com>
Copy link

github-actions bot commented Feb 2, 2025

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

github-actions bot pushed a commit that referenced this pull request Feb 2, 2025
Copy link
Collaborator

@schweitzpgi schweitzpgi left a comment

Choose a reason for hiding this comment

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

"Useless" sounds a little judgmental or something to me. Maybe WAW elimination or WAW removal sounds less aggressive? heh

@@ -975,6 +975,29 @@ def RegToMem : Pass<"regtomem", "mlir::func::FuncOp"> {
let dependentDialects = ["cudaq::cc::CCDialect", "quake::QuakeDialect"];
}

def RemoveUselessStores : Pass<"remove-useless-stores", "mlir::func::FuncOp"> {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I think we should call this "write-after-write-elimination`.

We can also make it a generic pass, since we may want to run it on more than func.func.

def RemoveUselessStores : Pass<"remove-useless-stores", "mlir::func::FuncOp"> {
let summary = "Remove stores that are overriden by other stores.";
let description = [{

Copy link
Collaborator

Choose a reason for hiding this comment

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

Can we add more words here? I think the example is good, but examples aren't the best documentation as they can leave some unanswered questions.

Comment on lines 133 to 146
// /// Detect if value is used in the op or its nested blocks.
// static bool isUsed(Value v, Operation *op) {
// for (auto opnd : op->getOperands())
// if (opnd == v)
// return true;

// for (auto &region : op->getRegions())
// for (auto &b : region)
// for (auto &innerOp : b)
// if (isUsed(v, &innerOp))
// return true;

// return false;
// }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove?

Comment on lines 77 to 86
// // Found a use for a current ptr before the overriding store
// if (currentPtr && isUsed(currentPtr, &op))
// return false;
}
}
// } else {
// // Found a use for a current ptr before the overriding store
// if (currentPtr && isUsed(currentPtr, &op))
// return false;
// }
Copy link
Collaborator

Choose a reason for hiding this comment

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

Remove?

Comment on lines 110 to 113
if (auto alloca = ptrOp.getDefiningOp<cudaq::cc::AllocaOp>())
return true;

return false;
Copy link
Collaborator

Choose a reason for hiding this comment

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

Suggested change
if (auto alloca = ptrOp.getDefiningOp<cudaq::cc::AllocaOp>())
return true;
return false;
return isa_and_present<cudaq::cc::AllocaOp>(ptrOp.getDefiningOp());


auto block = store.getOperation()->getBlock();
for (auto &op : *block) {
if (auto currentStore = dyn_cast<cudaq::cc::StoreOp>(&op)) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

I was thinking of doing this with an analysis step, first.

Have a DenseMap<Block*, DenseMap<Value, SmallVector<cudaq::cc::StoreOp*>>>
Scan the current operation's regions for cc::StoreOps. For each one found, add it to the map object by its block and ptr value argument.

After that go back over the map of maps, see if any of the vectors have > 1 element, and use dominance info to make sure no uses of the ptr value are dominated by the dominant store and dominate the post-dominant store other than any other stores in the block between the two (most dominant, most postdominant). Dominance here will catch uses into, e.g., cc::IfOp, etc.

If there is nothing but stores in the block between the two endpoint stores, go ahead and remove all but the postdominant one.

A more aggressive check would be to eliminate the dominant stores in a pair-wise, incremental approach. So if there is a store, if(with a load), store[*], store , the dead store[*] would be eliminated even though the first one cannot be removed.

Copy link
Collaborator Author

@annagrin annagrin Feb 3, 2025

Choose a reason for hiding this comment

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

I'll try that, thanks! I do need the most aggressive approach it seems from the uccsd examples

Signed-off-by: Anna Gringauze <agringauze@nvidia.com>
@annagrin annagrin changed the title Add RemoveUselessStores pass Add WriteAfterWriteElimination pass Feb 3, 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.

3 participants