@@ -1483,26 +1483,28 @@ struct AIEObjectFifoStatefulTransformPass
14831483 return nullptr;
14841484 }
14851485
1486- // True if op is nested inside an scf.if anywhere strictly below loopOp.
1486+ // True if op is nested inside an scf.if / scf.index_switch anywhere
1487+ // strictly below loopOp.
14871488 bool isInsideIfInsideLoop (Operation *op , Operation *loopOp ) {
14881489 Operation *cur = op->getParentOp();
14891490 while (cur && cur != loopOp) {
1490- if (isa<scf ::IfOp >(cur))
1491+ if (isa<scf ::IfOp , scf :: IndexSwitchOp >(cur))
14911492 return true ;
14921493 cur = cur->getParentOp();
14931494 }
14941495 return false ;
14951496 }
14961497
1497- // Returns the child region of the innermost scf.if that encloses `op`
1498- // strictly below `loopOp` (i.e. the specific then/else branch `op` lives
1499- // in), or null if `op` is not inside any conditional below loopOp. Used to
1500- // group conditional acq/rel by branch so per-branch balance can be checked.
1498+ // Returns the child region of the innermost scf.if / scf.index_switch that
1499+ // encloses `op` strictly below `loopOp` (i.e. the specific branch/case `op`
1500+ // lives in), or null if `op` is not inside any conditional below loopOp.
1501+ // Used to group conditional acq/rel by branch so the per-branch net can be
1502+ // compared across all branches of the conditional.
15011503 Region *immediateCondRegion (Operation *op , Operation *loopOp ) {
15021504 Operation *cur = op->getParentOp();
15031505 Region *childRegion = op->getParentRegion();
15041506 while (cur && cur != loopOp) {
1505- if (isa<scf ::IfOp >(cur))
1507+ if (isa<scf ::IfOp , scf :: IndexSwitchOp >(cur))
15061508 return childRegion;
15071509 childRegion = cur->getParentRegion();
15081510 cur = cur->getParentOp();
@@ -1608,26 +1610,31 @@ struct AIEObjectFifoStatefulTransformPass
16081610 }
16091611
16101612 // Decide whether loopOp's body has positive cyclostatic carry on at least
1611- // one (fifo, port). Diagnostics:
1612- // - an in-body acq/rel inside an scf.if whose branch is *unbalanced* for
1613- // a (fifo, port) that also has unconditional acq/rel -> *condDiag is set
1614- // and we return false (caller emits the diagnostic).
1613+ // one (fifo, port). Conditionals (scf.if / scf.index_switch) whose branches
1614+ // all agree on the same per-fifo net are folded into the static carry (the
1615+ // delta is branch-independent, hence deterministic); only conditionals
1616+ // whose branches disagree are treated as unanalyzable. Diagnostics:
1617+ // - an in-body acq/rel inside a conditional whose branches are *unbalanced*
1618+ // (disagree on the net) for a (fifo, port) that also has an analyzable
1619+ // contribution (unconditional acq/rel, or a foldable conditional net)
1620+ // -> *condDiag is set and we return false (caller emits the diagnostic).
16151621 // Sets `taintedDiag` if peel must be skipped because a fifo's pre-loop
1616- // held count cannot be analyzed (sibling loop / scf.if touched the same
1622+ // held count cannot be analyzed (sibling loop / conditional touched the same
16171623 // fifo) — this is silent, not an error, since the lowering still produces
16181624 // correct (just unoptimized) code.
16191625 bool bodyHasCyclostaticCarry (Region *bodyRegion , Operation *loopOp ,
16201626 Operation *&condDiag ) {
16211627 llvm::MapVector<FifoPort , int > maxAcq;
16221628 llvm::MapVector<FifoPort , int > sumRel;
1623- // Net (acquire - release) per (fifo, port) within each conditional
1624- // branch region. A branch that nets zero for a fifo (it acquires and
1625- // releases the same amount, e.g. an if that both acquires and releases)
1626- // does not perturb the loop's steady-state carry, so it is harmless even
1627- // when the same fifo is also used unconditionally.
1628- llvm::DenseMap<Region *, llvm ::MapVector <FifoPort , int >> condNet;
1629- // Conditional ops in walk order, for pointing the diagnostic at the
1630- // first offending op.
1629+ // For every conditional op (scf.if / scf.index_switch) directly below
1630+ // loopOp, accumulate the net (acquire - release) per (fifo, port) within
1631+ // each of its branch regions:
1632+ // condNet[condOp][branchRegion][fp] = net for that fifo on that path.
1633+ llvm::MapVector<Operation *,
1634+ llvm ::MapVector <Region *, llvm ::MapVector <FifoPort , int >>>
1635+ condNet;
1636+ // Conditional acq/rel ops in walk order, for pointing the diagnostic at
1637+ // the first offending op of a fifo.
16311638 SmallVector<std ::pair <Operation *, FifoPort >> condOps;
16321639 condDiag = nullptr;
16331640
@@ -1637,7 +1644,7 @@ struct AIEObjectFifoStatefulTransformPass
16371644 return;
16381645 FifoPort fp{a.getObjectFifo(), a.getPort()};
16391646 if (Region *r = immediateCondRegion(a , loopOp)) {
1640- condNet[r ][fp ] += a.acqNumber();
1647+ condNet[r -> getParentOp ()][ r ][fp ] += a.acqNumber();
16411648 condOps.push_back({a , fp });
16421649 } else {
16431650 maxAcq[fp ] = std::max(maxAcq.lookup(fp), a.acqNumber());
@@ -1647,35 +1654,96 @@ struct AIEObjectFifoStatefulTransformPass
16471654 return;
16481655 FifoPort fp{r.getObjectFifo(), r.getPort()};
16491656 if (Region *reg = immediateCondRegion(r , loopOp)) {
1650- condNet[reg ][fp ] -= r.relNumber();
1657+ condNet[reg -> getParentOp ()][ reg ][fp ] -= r.relNumber();
16511658 condOps.push_back({r , fp });
16521659 } else {
16531660 sumRel[fp ] = sumRel.lookup(fp) + r.relNumber();
16541661 }
16551662 }
16561663 });
16571664
1658- // A (fifo, port) is "unbalanced-conditional" if some conditional branch
1659- // has a nonzero net for it. Only such fifos make the straight-line carry
1660- // computation unsound, and only when they *also* have unconditional
1661- // acq/rel — then we cannot tell whether the conditional acquire/release
1662- // belongs to the steady state. Balanced conditional branches and
1663- // pure-conditional fifos (no unconditional ops) are well-formed: the
1664- // lock-lowering tracks each conditional path on its own without peeling.
1665- llvm::SetVector<FifoPort > unbalancedCond;
1666- for (auto ®ionEntry : condNet)
1667- for (auto &kv : regionEntry.second)
1668- if (kv.second != 0 )
1669- unbalancedCond.insert(kv.first);
1665+ // Classify each conditional op's contribution per (fifo, port).
1666+ //
1667+ // A conditional whose branches all agree on the same net C for a fifo
1668+ // contributes a *deterministic* per-iteration delta of C, regardless of
1669+ // which branch runs. That is indistinguishable from an unconditional
1670+ // acquire/release of net C, so it can be folded into the static carry.
1671+ // C == 0 (a branch that acquires and releases the same amount) is just
1672+ // the special case that adds nothing.
1673+ //
1674+ // When the branches *disagree* the per-iteration delta is data
1675+ // dependent, so the fifo is "unbalanced-conditional": the straight-line
1676+ // carry computation cannot account for it. Every branch region of the
1677+ // conditional participates — a branch that never touches the fifo (or an
1678+ // empty/absent else branch) counts as net 0, so an asymmetric
1679+ // "acquire on one path only" is correctly flagged.
1680+ llvm::MapVector<FifoPort , int > condCarry; // folded deterministic net
1681+ llvm::SetVector<FifoPort > unbalancedCond; // branches disagree
1682+ for (auto &opEntry : condNet) {
1683+ Operation *condOp = opEntry.first;
1684+ auto &branchMap = opEntry.second;
1685+ llvm::SetVector<FifoPort > touched;
1686+ for (auto ®Entry : branchMap)
1687+ for (auto &kv : regEntry.second)
1688+ touched.insert(kv.first);
1689+ for (const FifoPort &fp : touched) {
1690+ bool first = true , allEqual = true ;
1691+ int common = 0 ;
1692+ for (Region ® : condOp->getRegions()) {
1693+ int net = 0 ;
1694+ auto it = branchMap.find(®);
1695+ if (it != branchMap.end())
1696+ net = it->second.lookup(fp);
1697+ if (first) {
1698+ common = net;
1699+ first = false ;
1700+ } else if (net != common) {
1701+ allEqual = false ;
1702+ break;
1703+ }
1704+ }
1705+ if (allEqual)
1706+ condCarry[fp ] += common;
1707+ else
1708+ unbalancedCond.insert(fp);
1709+ }
1710+ }
16701711
1712+ // An unbalanced-conditional fifo is unanalyzable. We only *error* when it
1713+ // is mixed with an analyzable contribution for the same fifo — an
1714+ // unconditional acq/rel or a foldable conditional net — because then the
1715+ // peel would be based on an incomplete carry. A fifo that is *purely*
1716+ // unbalanced-conditional (no other use) is left alone: the lock-lowering
1717+ // tracks each path on its own and the loop still lowers correctly,
1718+ // just without the peel optimization.
16711719 for (const auto &fp : unbalancedCond) {
1672- if (maxAcq.count(fp) || sumRel.count(fp)) {
1720+ if (maxAcq.count(fp) || sumRel.count(fp) || condCarry.count(fp) ) {
16731721 for (const auto &co : condOps) {
16741722 if (co.second != fp)
16751723 continue;
16761724 Region *reg = immediateCondRegion(co.first , loopOp);
1677- auto it = reg ? condNet.find(reg) : condNet.end();
1678- if (it != condNet.end() && it->second.lookup(fp) != 0 ) {
1725+ Operation *condOp = reg ? reg->getParentOp() : nullptr;
1726+ // Point at an op living in a branch that disagrees with the rest.
1727+ auto opIt = condOp ? condNet.find(condOp) : condNet.end();
1728+ if (opIt == condNet.end())
1729+ continue;
1730+ bool opUnbalanced = false ;
1731+ int common = 0 ;
1732+ bool first = true ;
1733+ for (Region &r : condOp->getRegions()) {
1734+ int net = 0 ;
1735+ auto bit = opIt->second.find(&r);
1736+ if (bit != opIt->second.end())
1737+ net = bit->second.lookup(fp);
1738+ if (first) {
1739+ common = net;
1740+ first = false ;
1741+ } else if (net != common) {
1742+ opUnbalanced = true ;
1743+ break;
1744+ }
1745+ }
1746+ if (opUnbalanced) {
16791747 condDiag = co.first;
16801748 break;
16811749 }
@@ -1684,13 +1752,24 @@ struct AIEObjectFifoStatefulTransformPass
16841752 }
16851753 }
16861754
1755+ // Fold the deterministic conditional net into the static carry. For a
1756+ // pure-conditional fifo whose branches all agree, this is the whole
1757+ // carry (maxAcq/sumRel are zero); the peel still clones the entire
1758+ // iteration 0 including the conditional, so AcquireGreaterEqual clamping
1759+ // keeps it correct.
1760+ llvm::SetVector<FifoPort > fifos;
1761+ for (const auto &kv : maxAcq)
1762+ fifos.insert(kv.first);
1763+ for (const auto &kv : condCarry)
1764+ fifos.insert(kv.first);
1765+
16871766 llvm::SetVector<FifoPort > tainted;
16881767 auto held = heldBeforeLoop(loopOp , tainted);
1689- for (const auto &kv : maxAcq ) {
1690- if (tainted.contains(kv.first ))
1768+ for (const FifoPort &fp : fifos ) {
1769+ if (tainted.contains(fp ))
16911770 continue;
1692- int carry = kv.second - sumRel.lookup(kv.first );
1693- int peelCarry = carry - held.lookup(kv.first );
1771+ int carry = maxAcq.lookup(fp) - sumRel.lookup(fp) + condCarry.lookup(fp );
1772+ int peelCarry = carry - held.lookup(fp );
16941773 if (peelCarry > 0 )
16951774 return true ;
16961775 }
0 commit comments