@@ -288,31 +288,112 @@ struct LowerProcessesPass
288288 void runOnOperation () override ;
289289};
290290
291- // Perform cleanup on the process operation prior to lowering.
292- // This can remove redundant operands passed to the successor of llhd.wait.
293- //
294- // ^bb0
295- // cf.br ^bb1(%clock : i1)
296- // ^bb1(%1: i1): // pred: ^bb0, ^bb2
297- // llhd.wait yield (...), (%clock : i1), ^bb2(%1 : i1)
298- // ^bb2(%2: i1): // pred: ^bb1
299- // cf.cond_br %true, ^bb3, ^bb1(%2 : i1)
300- // ^bb3:
301- //
302- // The above IR can be rewritten as:
303- //
304- // ^bb0
305- // cf.br ^bb1
306- // ^bb1: // pred: ^bb0, ^bb2
307- // llhd.wait yield (...), (%clock : i1), ^bb2
308- // ^bb2: // pred: ^bb1
309- // cf.cond_br %true, ^bb3, ^bb1
310- // ^bb3:
311- //
291+ static LogicalResult dropRedundantArguments (RewriterBase &rewriter,
292+ Block &block) {
293+ SmallVector<size_t > argsToErase;
294+
295+ // Go through the arguments of the block.
296+ for (auto [argIdx, blockOperand] : llvm::enumerate (block.getArguments ())) {
297+ bool sameArg = true ;
298+ Value commonValue;
299+
300+ // Go through the block predecessor and flag if they pass to the block
301+ // different values for the same argument.
302+ for (Block::pred_iterator predIt = block.pred_begin (),
303+ predE = block.pred_end ();
304+ predIt != predE; ++predIt) {
305+ Operation *terminator = (*predIt)->getTerminator ();
306+ auto branchOperand =
307+ llvm::TypeSwitch<Operation *, std::optional<Value>>(terminator)
308+ .Case ([&, argIdx = argIdx](BranchOpInterface branchOp) {
309+ auto succIndex = predIt.getSuccessorIndex ();
310+ SuccessorOperands succOperands =
311+ branchOp.getSuccessorOperands (succIndex);
312+ auto branchOperands = succOperands.getForwardedOperands ();
313+ return branchOperands[argIdx];
314+ })
315+ .Case ([&, argIdx = argIdx](WaitOp waitOp) {
316+ auto destOperands = waitOp.getDestOperands ();
317+ return destOperands[argIdx];
318+ })
319+ .Default ([](Operation *) { return std::nullopt ; });
320+
321+ if (!branchOperand.has_value ()) {
322+ sameArg = false ;
323+ break ;
324+ }
325+
326+ if (!commonValue) {
327+ commonValue = *branchOperand;
328+ continue ;
329+ }
330+
331+ if (*branchOperand != commonValue) {
332+ sameArg = false ;
333+ break ;
334+ }
335+ }
336+
337+ // If they are passing the same value, drop the argument.
338+ if (commonValue && sameArg) {
339+ argsToErase.push_back (argIdx);
340+
341+ // Remove the argument from the block.
342+ rewriter.replaceAllUsesWith (blockOperand, commonValue);
343+ }
344+ }
345+
346+ // Remove the arguments.
347+ for (size_t argIdx : llvm::reverse (argsToErase)) {
348+ block.eraseArgument (argIdx);
349+
350+ // Remove the argument from the branch ops.
351+ for (auto predIt = block.pred_begin (), predE = block.pred_end ();
352+ predIt != predE; ++predIt) {
353+ llvm::TypeSwitch<Operation *, void >((*predIt)->getTerminator ())
354+ .Case ([&](BranchOpInterface branchOp) {
355+ auto branch = cast<BranchOpInterface>((*predIt)->getTerminator ());
356+ unsigned succIndex = predIt.getSuccessorIndex ();
357+ SuccessorOperands succOperands =
358+ branch.getSuccessorOperands (succIndex);
359+ succOperands.erase (argIdx);
360+ })
361+ .Case ([&](WaitOp waitOp) {
362+ auto destOperands = waitOp.getDestOperandsMutable ();
363+ destOperands.erase (argIdx);
364+ })
365+ .Default ([](Operation *) {
366+ llvm_unreachable (" Unexpected predecessor terminator" );
367+ });
368+ }
369+ }
370+
371+ return success (!argsToErase.empty ());
372+ }
373+
312374static void simplifyProcess (ProcessOp &processOp) {
313375 OpBuilder builder (processOp);
314376 IRRewriter rewriter (builder);
315377 (void )simplifyRegions (rewriter, processOp->getRegions ());
378+
379+ // simplifyRegions does not prune the destination operands of the
380+ // `llhd.wait` operation because it does not implement BranchOpInterface, due
381+ // to its side-effect semantics. Implementing BranchOpInterface could allow
382+ // other optimizations to forward branch operands from the llhd.wait
383+ // operation to the destination block where execution resumes. However, this
384+ // would be invalid, as the SSA value might change across the wait operation.
385+ // Therefore, we manually prune the destination block arguments ourselves.
386+ for (auto &block : processOp.getBody ()) {
387+ auto waitOp = dyn_cast<WaitOp>(block.getTerminator ());
388+ if (!waitOp)
389+ continue ;
390+
391+ auto dstOperands = waitOp.getDestOperands ();
392+ if (dstOperands.empty ())
393+ continue ;
394+
395+ (void )dropRedundantArguments (rewriter, *waitOp.getDest ());
396+ }
316397}
317398
318399} // namespace
0 commit comments