Skip to content

infer pipe init nosplit from split-0 users#452

Draft
zhangstevenunity wants to merge 1 commit intomainfrom
codex/pipe-init-nosplit
Draft

infer pipe init nosplit from split-0 users#452
zhangstevenunity wants to merge 1 commit intomainfrom
codex/pipe-init-nosplit

Conversation

@zhangstevenunity
Copy link
Copy Markdown
Collaborator

What changed

Infer implicit nosplit = true only on internal pipe init ops when any same-logical-pipe pto.tpush, pto.tpop, or pto.tfree in the module uses split = 0.

Why

The init-side nosplit setting should be derived from pipe usage semantics instead of being carried as an explicit push/pop frontend parameter.

Impact

Generated TPipe<...> init templates now append the final true or false flag based on split-0 usage on the same pipe, while frontend and internal push/pop ops continue to use only the existing split attribute.

Root cause

The previous implementation direction risked spreading nosplit onto data ops, but the intended behavior is to keep it implicit and init-scoped.

Validation

  • Updated existing EmitC/FileCheck expectations for tpush/tpop pipe init templates
  • Added test/basic/tpush_tpop_frontend_nosplit_a5.pto
  • Did not run ptoas locally because no usable local binary/build was available in this environment

Copy link
Copy Markdown

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a nosplit attribute to pipe initialization operations and implements inference logic within the PTOResolveReservedBuffers pass to automatically set this attribute when downstream pipe users have a split value of 0. The EmitC backend is updated to include this boolean in the TPipe template parameters. Feedback suggests refactoring the pipe user inference loop for better readability and deduplicating the logic for resolving standalone nosplit attributes using a templated helper function.

Comment on lines +86 to +102
for (Operation *user : pipe.getUsers()) {
if (auto pushOp = dyn_cast<TPushOp>(user)) {
if (pushOp.getSplit() == 0)
return true;
continue;
}
if (auto popOp = dyn_cast<TPopOp>(user)) {
if (popOp.getSplit() == 0)
return true;
continue;
}
if (auto freeOp = dyn_cast<TFreeOp>(user)) {
if (freeOp.getSplit() == 0)
return true;
}
}
return false;
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The series of if statements with continue can be refactored into a more concise if-else if chain. This improves readability and is slightly more efficient as it avoids redundant checks. The continue statements are also unnecessary since the if blocks return immediately.

  for (Operation *user : pipe.getUsers()) {
    if (auto pushOp = dyn_cast<TPushOp>(user)) {
      if (pushOp.getSplit() == 0)
        return true;
    } else if (auto popOp = dyn_cast<TPopOp>(user)) {
      if (popOp.getSplit() == 0)
        return true;
    } else if (auto freeOp = dyn_cast<TFreeOp>(user)) {
      if (freeOp.getSplit() == 0)
        return true;
    }
  }
  return false;

Comment on lines +281 to +292
moduleOp.walk([&](InitializeL2LPipeOp initOp) {
if (groupedNoSplitResolved.count(initOp.getOperation()))
return;
if (inferNoSplitFromPipeUsers(initOp.getPipe()))
setNoSplitAttr(initOp, builder.getBoolAttr(true));
});
moduleOp.walk([&](InitializeL2G2LPipeOp initOp) {
if (groupedNoSplitResolved.count(initOp.getOperation()))
return;
if (inferNoSplitFromPipeUsers(initOp.getPipe()))
setNoSplitAttr(initOp, builder.getBoolAttr(true));
});
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The logic inside the two moduleOp.walk calls for InitializeL2LPipeOp and InitializeL2G2LPipeOp is identical. This code duplication can be avoided by extracting the logic into a templated helper function, which would improve maintainability.

For example, you could define a helper function:

template <typename InitOpT>
void resolveStandaloneNoSplit(InitOpT initOp,
                              const std::set<Operation *> &resolvedOps,
                              OpBuilder &builder) {
  if (resolvedOps.count(initOp.getOperation())) {
    return;
  }
  if (inferNoSplitFromPipeUsers(getPipeResult(initOp))) {
    setNoSplitAttr(initOp, builder.getBoolAttr(true));
  }
}

And then call it from the walks:

moduleOp.walk([&](InitializeL2LPipeOp initOp) {
  resolveStandaloneNoSplit(initOp, groupedNoSplitResolved, builder);
});
moduleOp.walk([&](InitializeL2G2LPipeOp initOp) {
  resolveStandaloneNoSplit(initOp, groupedNoSplitResolved, builder);
});

@zhangstevenunity zhangstevenunity changed the title [codex] infer pipe init nosplit from split-0 users infer pipe init nosplit from split-0 users Apr 7, 2026
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.

1 participant