From 1bcf3a6eed0dbe19d7809058af36fbe3060638fa Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Thu, 16 Oct 2025 01:23:49 +0200 Subject: [PATCH 01/15] Main --- .../lib/Support/FtdImplementation.cpp | 415 ++++++------------ .../Analysis/ControlDependenceAnalysis.h | 7 + lib/Analysis/ControlDependenceAnalysis.cpp | 236 +++++++++- 3 files changed, 387 insertions(+), 271 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index c7ca948a71..038dc0a9ee 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -28,24 +28,6 @@ using namespace dynamatic; using namespace dynamatic::experimental; using namespace dynamatic::experimental::boolean; -/// Different types of loop suppression. -enum BranchToLoopType { - - // In this case, the producer is inside a loop, while the consumer is outside. - // The token must be suppressed as long as the loop is executed, in order to - // provide only the final token handled. - MoreProducerThanConsumers, - - // In this case, the producer is the consumer itself; this is the case of a - // regeneration multiplexer. The token must be suppressed only if the loop is - // done iterating. - SelfRegeneration, - - // In this case, the token is used back in a loop. The token is to be - // suppressed only if the loop is done iterating. - BackwardRelationship -}; - /// Annotation to use in the IR when an operation needs to be skipped by the FTD /// algorithm. constexpr llvm::StringLiteral FTD_OP_TO_SKIP("ftd.skip"); @@ -178,58 +160,6 @@ static void eliminateCommonBlocks(DenseSet &s1, } } -/// Given an operation, returns true if the operation is a conditional branch -/// which terminates a for loop. This is the case if it is in one of the exiting -/// blocks of the innermost loop it is in. -static bool isBranchLoopExit(Operation *op, CFGLoopInfo &li) { - if (isa(op)) { - if (CFGLoop *loop = li.getLoopFor(op->getBlock()); loop) { - llvm::SmallVector exitBlocks; - loop->getExitingBlocks(exitBlocks); - return llvm::find(exitBlocks, op->getBlock()) != exitBlocks.end(); - } - } - return false; -} - -/// Given an operation, return true if the two operands of a multiplexer come -/// from two different loops. When this happens, the mux is connecting two -/// loops. -static bool isaMuxLoop(Operation *mux, CFGLoopInfo &li) { - - auto muxOp = llvm::dyn_cast(mux); - if (!muxOp) - return false; - - auto dataOperands = muxOp.getDataOperands(); - - // Get the basic block of the "real" value, so going up the hierarchy as long - // as there are conditional branches involved. - auto getBasicBlockProducer = [&](Value op) -> Block * { - Block *bb = op.getParentBlock(); - - // If the operand is produced by a real operation, such operation might be a - // conditional branch in the same bb of the original. - if (auto *owner = op.getDefiningOp(); owner) { - while (llvm::isa_and_nonnull(owner) && - owner->getBlock() == muxOp->getBlock()) { - auto op = dyn_cast(owner); - if (op.getOperand(1).getDefiningOp()) { - owner = op.getOperand(1).getDefiningOp(); - bb = owner->getBlock(); - continue; - } - break; - } - } - - return bb; - }; - - return li.getLoopFor(getBasicBlockProducer(dataOperands[0])) != - li.getLoopFor(getBasicBlockProducer(dataOperands[1])); -} - /// The boolean condition to either generate or suppress a token are computed /// by considering all the paths from the producer (`start`) to the consumer /// (`end`). "Each path identifies a Boolean product of elementary conditions @@ -265,29 +195,6 @@ static BoolExpression *enumeratePaths(Block *start, Block *end, return sop->boolMinimizeSop(); } -/// Get a boolean expression representing the exit condition of the current -/// loop block. -static BoolExpression *getBlockLoopExitCondition(Block *loopExit, CFGLoop *loop, - CFGLoopInfo &li, - const ftd::BlockIndexing &bi) { - - // Get the boolean expression associated to the block exit - BoolExpression *blockCond = - BoolExpression::parseSop(bi.getBlockCondition(loopExit)); - - // Since we are in a loop, the terminator is a conditional branch. - auto *terminatorOperation = loopExit->getTerminator(); - auto condBranch = dyn_cast(terminatorOperation); - assert(condBranch && "Terminator of a loop must be `cf::CondBranchOp`"); - - // If the destination of the false outcome is not the block, then the - // condition must be negated - if (li.getLoopFor(condBranch.getFalseDest()) != loop) - blockCond->boolNegate(); - - return blockCond; -} - /// Run the Cytron algorithm to determine, give a set of values, in which blocks /// should we add a merge in order for those values to be merged static DenseSet @@ -748,119 +655,6 @@ static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, return muxOp.getResult(); } -// Returns true if loop is a while loop, detected by the loop header being -// also a loop exit and not a loop latch -static bool isWhileLoop(CFGLoop *loop) { - if (!loop) - return false; - - Block *headerBlock = loop->getHeader(); - - SmallVector exitBlocks; - loop->getExitingBlocks(exitBlocks); - - SmallVector latchBlocks; - loop->getLoopLatches(latchBlocks); - - return llvm::is_contained(exitBlocks, headerBlock) && - !llvm::is_contained(latchBlocks, headerBlock); -} - -using PairOperandConsumer = std::pair; - -/// Insert a branch to the correct position, taking into account whether it -/// should work to suppress the over-production of tokens or self-regeneration -static Value addSuppressionInLoop(PatternRewriter &rewriter, CFGLoop *loop, - Operation *consumer, Value connection, - BranchToLoopType btlt, CFGLoopInfo &li, - std::vector &toCover, - const ftd::BlockIndexing &bi) { - - handshake::ConditionalBranchOp branchOp; - - // Case in which there is only one termination block - if (Block *loopExit = loop->getExitingBlock(); loopExit) { - - // Do not add the branch in case of a while loop with backward edge - if (btlt == BackwardRelationship && isWhileLoop(loop)) - return connection; - - // Get the termination operation, which is supposed to be conditional - // branch. - Operation *loopTerminator = loopExit->getTerminator(); - assert(isa(loopTerminator) && - "Terminator condition of a loop exit must be a conditional " - "branch."); - - // A conditional branch is now to be added next to the loop terminator, so - // that the token can be suppressed - auto *exitCondition = getBlockLoopExitCondition(loopExit, loop, li, bi); - auto conditionValue = - boolVariableToCircuit(rewriter, exitCondition, loopExit, bi); - - rewriter.setInsertionPointToStart(loopExit); - - // Since only one output is used, the other one will be connected to sink - // in the materialization pass, as we expect from a suppress branch - branchOp = rewriter.create( - loopExit->getOperations().back().getLoc(), - ftd::getListTypes(connection.getType()), conditionValue, connection); - - } else { - - std::vector cofactorList; - SmallVector exitBlocks; - loop->getExitingBlocks(exitBlocks); - loopExit = exitBlocks.front(); - - BoolExpression *fLoopExit = BoolExpression::boolZero(); - - // Get the list of all the cofactors related to possible exit conditions - for (Block *exitBlock : exitBlocks) { - BoolExpression *blockCond = - getBlockLoopExitCondition(exitBlock, loop, li, bi); - fLoopExit = BoolExpression::boolOr(fLoopExit, blockCond); - cofactorList.push_back(bi.getBlockCondition(exitBlock)); - } - - // Sort the cofactors alphabetically - std::sort(cofactorList.begin(), cofactorList.end()); - - // Apply a BDD expansion to the loop exit expression and the list of - // cofactors - BDD *bdd = buildBDD(fLoopExit, cofactorList); - - // Convert the boolean expression obtained through BDD to a circuit - Value branchCond = bddToCircuit(rewriter, bdd, loopExit, bi); - - Operation *loopTerminator = loopExit->getTerminator(); - assert(isa(loopTerminator) && - "Terminator condition of a loop exit must be a conditional " - "branch."); - - rewriter.setInsertionPointToStart(loopExit); - - branchOp = rewriter.create( - loopExit->getOperations().front().getLoc(), - ftd::getListTypes(connection.getType()), branchCond, connection); - } - - Value newConnection = btlt == MoreProducerThanConsumers - ? branchOp.getTrueResult() - : branchOp.getFalseResult(); - - // If we are handling a case with more producers than consumers, the new - // branch must undergo the `addSupp` function so we add it to our structure - // to be able to loop over it - if (btlt == MoreProducerThanConsumers) { - branchOp->setAttr(FTD_NEW_SUPP, rewriter.getUnitAttr()); - toCover.emplace_back(newConnection, consumer); - } - - consumer->replaceUsesOfWith(connection, newConnection); - return newConnection; -} - /// Apply the algorithm from FPL'22 to handle a non-loop situation of /// producer and consumer static void insertDirectSuppression( @@ -887,6 +681,45 @@ static void insertDirectSuppression( // Get the control dependencies from the consumer DenseSet consControlDeps = cdAnalysis[consumer->getBlock()].forwardControlDeps; + + llvm::errs() << "[FTD] Producer block: "; + if (producerBlock) + producerBlock->printAsOperand(llvm::errs()); + else + llvm::errs() << "(null)"; + llvm::errs() << ", Consumer block: "; + if (consumerBlock) + consumerBlock->printAsOperand(llvm::errs()); + else + llvm::errs() << "(null)"; + llvm::errs() << "\n"; + // Debug: dump consumer block control deps + { + Block *consumerBlock = consumer->getBlock(); + auto &prodEntry = cdAnalysis[producerBlock]; + auto &depsEntry = cdAnalysis[consumerBlock]; + + auto printBlockSet = [&](llvm::StringRef label, + const DenseSet &S) { + llvm::errs() << label << " = { "; + bool first = true; + for (Block *b : S) { + if (!first) + llvm::errs() << ", "; + if (b) + b->printAsOperand(llvm::errs()); + else + llvm::errs() << ""; + first = false; + } + llvm::errs() << " }\n"; + }; + printBlockSet("[FTD] prod forwardControlDeps", + prodEntry.forwardControlDeps); + printBlockSet("[FTD] cons forwardControlDeps", + depsEntry.forwardControlDeps); + printBlockSet("[FTD] cons allControlDeps", depsEntry.allControlDeps); + } // If the mux condition is to be taken into account, then the control // dependencies of the mux conditions are to be added to the consumer control @@ -903,21 +736,78 @@ static void insertDirectSuppression( // Get rid of common entries in the two sets eliminateCommonBlocks(prodControlDeps, consControlDeps); + DenseSet locConsControlDeps = + getLocalConsDependence(producerBlock, consumerBlock); // Compute the activation function of producer and consumer BoolExpression *fProd = enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); BoolExpression *fCons = - enumeratePaths(entryBlock, consumerBlock, bi, consControlDeps); + enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps); + + { + auto printBlockSet = [&](llvm::StringRef label, + const DenseSet &S) { + llvm::errs() << label << " = { "; + bool first = true; + for (Block *b : S) { + if (!first) + llvm::errs() << ", "; + if (b) + b->printAsOperand(llvm::errs()); + else + llvm::errs() << ""; + first = false; + } + llvm::errs() << " }\n"; + }; + + printBlockSet("[FTD] locConsControlDeps", locConsControlDeps); + } + + if (accountMuxCondition) { + muxCondition = consumer->getOperand(0); + Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); + DenseSet condControlDeps = + cdAnalysis[muxConditionBlock].forwardControlDeps; + { + auto printBlockSet = [&](llvm::StringRef label, + const DenseSet &S) { + llvm::errs() << label << " = { "; + bool first = true; + for (Block *b : S) { + if (!first) + llvm::errs() << ", "; + if (b) + b->printAsOperand(llvm::errs()); + else + llvm::errs() << ""; + first = false; + } + llvm::errs() << " }\n"; + }; + + printBlockSet("[FTD] muxControlDeps", condControlDeps); + } + // for (auto &x : condControlDeps) + // locConsControlDeps.insert(x); + } if (accountMuxCondition) { Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); BoolExpression *selectOperandCondition = BoolExpression::parseSop(bi.getBlockCondition(muxConditionBlock)); + llvm::errs() << "[MUX] Mux Condition Block: "; + if (muxConditionBlock) + muxConditionBlock->printAsOperand(llvm::errs()); + else + llvm::errs() << "(null)"; + llvm::errs() << "\n"; + // The condition must be taken into account for `fCons` only if the // producer is not control dependent from the block which produces the // condition of the mux - if (!prodControlDeps.contains(muxConditionBlock)) { + if (!bi.isLess(muxConditionBlock, producerBlock)) { if (consumer->getOperand(1) == connection) fCons = BoolExpression::boolAnd(fCons, selectOperandCondition->boolNegate()); @@ -926,9 +816,55 @@ static void insertDirectSuppression( } } + if (llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + (llvm::isa(consumer) && + !llvm::isa(consumer)) || + (llvm::isa(consumer) && + !llvm::isa(consumer))) + return; + /// f_supp = f_prod and not f_cons - BoolExpression *fSup = BoolExpression::boolAnd(fProd, fCons->boolNegate()); + llvm::errs() << "fProd = " << fProd->toString() << "\n"; + llvm::errs() << "fCons = " << fCons->toString() << "\n"; + if (fProd->type == experimental::boolean::ExpressionType::Zero) + return; + BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); + llvm::errs() << "fSupmin = " << fSup->toString() << "\n"; + if (fProd->type == experimental::boolean::ExpressionType::Zero) { + llvm::errs() << "[FTD] fProd == 0 detected\n"; + llvm::errs() << " producer block: "; + if (producerBlock) + producerBlock->printAsOperand(llvm::errs()); + else + llvm::errs() << ""; + llvm::errs() << "\n"; + + llvm::errs() << " consumer block: "; + if (consumerBlock) + consumerBlock->printAsOperand(llvm::errs()); + else + llvm::errs() << ""; + llvm::errs() << "\n"; + + llvm::errs() << " producer (connection): "; + if (Operation *def = connection.getDefiningOp()) { + llvm::errs() << def->getName() << " @ "; + def->getLoc().print(llvm::errs()); + } else { + llvm::errs() << ""; + } + llvm::errs() << "\n"; + + llvm::errs() << " consumer: " << consumer->getName() << " @ "; + consumer->getLoc().print(llvm::errs()); + llvm::errs() << "\n"; + } // If the activation function is not zero, then a suppress block is to be // inserted @@ -1026,67 +962,6 @@ void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, !llvm::isa(consumerOp)) || llvm::isa(operand.getType())) return; - - // The next step is to identify the relationship between the producer - // and consumer in hand: Are they in the same loop or at different - // loop levels? Are they connected through a backward edge? - - // Set true if the producer is in a loop which does not contains - // the consumer - bool producingGtUsing = - loopInfo.getLoopFor(producerBlock) && - !loopInfo.getLoopFor(producerBlock)->contains(consumerBlock); - - auto *consumerLoop = loopInfo.getLoopFor(consumerBlock); - std::vector newToCover; - - // Set to true if the consumer uses its own result - bool selfRegeneration = - llvm::any_of(consumerOp->getResults(), - [&operand](const Value &v) { return v == operand; }); - - // We need to suppress all the tokens produced within a loop and - // used outside each time the loop is not terminated. This should be - // done for as many loops there are - if (producingGtUsing && !isBranchLoopExit(producerOp, loopInfo)) { - Value con = operand; - for (CFGLoop *loop = loopInfo.getLoopFor(producerBlock); loop; - loop = loop->getParentLoop()) { - - // For each loop containing the producer but not the consumer, add - // the branch - if (!loop->contains(consumerBlock)) - con = addSuppressionInLoop(rewriter, loop, consumerOp, con, - MoreProducerThanConsumers, loopInfo, - newToCover, bi); - } - - for (auto &pair : newToCover) - addSuppOperandConsumer(rewriter, funcOp, pair.second, pair.first); - - return; - } - - // We need to suppress a token if the consumer is the producer itself - // within a loop - if (selfRegeneration && consumerLoop && - !producerOp->hasAttr(FTD_NEW_SUPP)) { - addSuppressionInLoop(rewriter, consumerLoop, consumerOp, operand, - SelfRegeneration, loopInfo, newToCover, bi); - return; - } - - // We need to suppress a token if the consumer comes before the - // producer (backward edge) - if ((bi.isGreater(producerBlock, consumerBlock) || - (llvm::isa(consumerOp) && - producerBlock == consumerBlock && - isaMuxLoop(consumerOp, loopInfo))) && - consumerLoop) { - addSuppressionInLoop(rewriter, consumerLoop, consumerOp, operand, - BackwardRelationship, loopInfo, newToCover, bi); - return; - } } // Handle the suppression in all the other cases (including the operand being diff --git a/include/dynamatic/Analysis/ControlDependenceAnalysis.h b/include/dynamatic/Analysis/ControlDependenceAnalysis.h index 8fe762aebc..3a9560b92d 100644 --- a/include/dynamatic/Analysis/ControlDependenceAnalysis.h +++ b/include/dynamatic/Analysis/ControlDependenceAnalysis.h @@ -76,6 +76,13 @@ class ControlDependenceAnalysis { void addDepsOfDeps(Region ®ion); }; +/// Build a local CDG on the subgraph rooted at `prod` with a single sink node +/// Sk. Paths stop when reaching: a real end, the second visit to `prod`, or +/// `cons`. If `prod == cons`, the very first hit at the start does not stop. +/// The function returns the set of all control-ancestors of `cons` in this +/// local CDG. +DenseSet getLocalConsDependence(Block *prod, Block *cons); + } // namespace dynamatic #endif // DYNAMATIC_ANALYSIS_CONTROLDEPENDENCEANALYSIS_H diff --git a/lib/Analysis/ControlDependenceAnalysis.cpp b/lib/Analysis/ControlDependenceAnalysis.cpp index 492ab1345e..5d564553aa 100644 --- a/lib/Analysis/ControlDependenceAnalysis.cpp +++ b/lib/Analysis/ControlDependenceAnalysis.cpp @@ -226,12 +226,246 @@ dynamatic::ControlDependenceAnalysis::getAllBlockDeps() const { return blocksControlDeps; } +DenseSet dynamatic::getLocalConsDependence(Block *prod, Block *cons) { + if (!prod || !cons) + return DenseSet{}; + Region *reg = prod->getParent(); + if (!reg || reg != cons->getParent()) + return DenseSet{}; + + // 1) Build the local subgraph G' with a single sink node Sk + struct NodeInfo { + SmallVector succs; // Successors in G' (excluding Sk) + bool toSink = false; // Whether this node has an edge to Sk + }; + + DenseMap G; // Only contains blocks in the local subgraph + Block *SINK = nullptr; // Use nullptr to represent Sk (the unique exit) + + enum class Mark : int { kUnseen = 0, kSeen, kDone }; + DenseMap mark; + + // Construction rules: + // 1) Keep all edges u -> cons. The block `cons` itself is not expanded, + // but must always have an outgoing edge cons -> Sk. + // 2) For any successor s == prod: + // - If prod == cons, connect u -> cons (and cons will later connect to + // Sk). + // - If prod != cons, connect u -> Sk (this represents the second visit + // to prod). + // 3) For any block with no successors or successors outside the current + // region, + // connect that block -> Sk. + // 4) If prod == cons and b == cons at the starting point (isStart == true), + // do NOT stop; expand successors once normally. + std::function build = [&](Block *b, bool isStart) { + (void)G[b]; // Ensure the node exists in the map + + // Case: current block is `cons` + // Always add an edge cons -> Sk + if (b == cons) { + G[b].toSink = true; // cons -> Sk always exists + // At the starting node (prod == cons, first visit): keep exploring + if (!(isStart && prod == cons)) { + // For non-start or when prod != cons, stop expanding at cons + mark[b] = Mark::kDone; + return; + } + // Otherwise continue expanding successors normally + } + + if (mark[b] == Mark::kDone) + return; + if (mark[b] == Mark::kSeen && !isStart) + return; + mark[b] = Mark::kSeen; + + auto succRange = b->getSuccessors(); + if (succRange.empty()) { + // True end block: connect to Sk + G[b].toSink = true; + mark[b] = Mark::kDone; + return; + } + + for (Block *s : succRange) { + // Out-of-region successors are treated as -> Sk + if (!s || s->getParent() != reg) { + G[b].toSink = true; + continue; + } + + // Successor is `prod` + if (s == prod) { + if (prod == cons) { + // Second visit to prod is treated as reaching cons. + // First connect to cons, then cons -> Sk will terminate the path. + (void)G[cons]; + G[b].succs.push_back(cons); + } else { + // prod != cons: second visit to prod terminates directly -> Sk + G[b].toSink = true; + } + continue; + } + + // Successor is `cons`: keep the edge but do not expand cons + if (s == cons) { + (void)G[cons]; + G[b].succs.push_back(cons); + continue; // Do not recursively expand cons + } + + // Normal edge b -> s + G[b].succs.push_back(s); + build(s, /*isStart=*/false); + } + + mark[b] = Mark::kDone; + }; + + build(prod, /*isStart=*/true); + + // If cons is unreachable in the local subgraph, return an empty set + if (!G.contains(cons)) + return DenseSet{}; + + // Expand all "toSink" edges into explicit -> Sk edges + SmallVector nodes; + nodes.reserve(G.size() + 1); + for (auto &kv : G) + nodes.push_back(kv.first); + nodes.push_back(SINK); + + DenseMap> succP, predP; + for (Block *n : nodes) { + succP[n]; + predP[n]; + } + for (auto &kv : G) { + Block *u = kv.first; + for (Block *v : kv.second.succs) { + succP[u].push_back(v); + predP[v].push_back(u); + } + if (kv.second.toSink) { + succP[u].push_back(SINK); + predP[SINK].push_back(u); + } + } + + // 2) Compute post-dominance sets with respect to Sk + DenseMap> postdom; + for (Block *n : nodes) { + if (n == SINK) { + postdom[n].insert(SINK); + } else { + for (Block *m : nodes) + postdom[n].insert(m); + } + } + + bool changed = true; + while (changed) { + changed = false; + for (Block *n : nodes) { + if (n == SINK) + continue; + + // inter = intersection of postdom(s) for all successors s of n + DenseSet inter; + bool first = true; + for (Block *s : succP[n]) { + if (first) { + inter = postdom[s]; + first = false; + } else { + DenseSet tmp; + for (Block *x : inter) + if (postdom[s].contains(x)) + tmp.insert(x); + inter.swap(tmp); + } + } + + DenseSet newSet = inter; + newSet.insert(n); + + // Check for changes + if (newSet.size() != postdom[n].size()) { + postdom[n].swap(newSet); + changed = true; + continue; + } + for (Block *x : newSet) { + if (!postdom[n].contains(x)) { + postdom[n].swap(newSet); + changed = true; + break; + } + } + } + } + + auto postDominates = [&](Block *x, Block *y) -> bool { + auto it = postdom.find(y); + return it != postdom.end() && it->second.contains(x); + }; + + // 3) Query CDG direct predecessors (without explicitly building CDG) + // Ferrante–Ottenstein–Warren criterion: + // ∃ Y->Z such that X ∈ postdom(Z) and X ∉ postdom(Y) + // -> add edge Y -> X in CDG + auto predsInCDG = [&](Block *X, DenseSet &out) { + for (Block *Y : nodes) { + if (Y == SINK) + continue; + if (postDominates(X, Y)) + continue; + for (Block *Z : succP[Y]) { + if (postDominates(X, Z)) { + out.insert(Y); + break; + } + } + } + }; + + // 4) Reverse closure: collect all control ancestors of `cons` + DenseSet upstream, frontier; + SmallVector stack; + + // Direct control predecessors of cons + predsInCDG(cons, frontier); + for (Block *p : frontier) { + upstream.insert(p); + stack.push_back(p); + } + + // BFS/DFS over CDG to get transitive closure (all upstream control points) + while (!stack.empty()) { + Block *x = stack.back(); + stack.pop_back(); + DenseSet parents; + predsInCDG(x, parents); + for (Block *p : parents) { + if (!upstream.contains(p)) { + upstream.insert(p); + stack.push_back(p); + } + } + } + + return upstream; +} + void dynamatic::ControlDependenceAnalysis::printAllBlocksDeps() const { DEBUG_WITH_TYPE( "CONTROL_DEPENDENCY_ANALYSIS", llvm::dbgs() << "\n*********************************\n\n"; - for (auto &elem : blocksControlDeps) { + for (auto &elem + : blocksControlDeps) { Block *block = elem.first; block->printAsOperand(llvm::dbgs()); llvm::dbgs() << " is control dependent on: "; From a15919b7a8080c2de903b3e24d775dbbac1734de Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sat, 18 Oct 2025 02:13:38 +0200 Subject: [PATCH 02/15] Add temporary debug outputs --- experimental/lib/Support/FtdImplementation.cpp | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index 038dc0a9ee..b8d3f2049b 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -791,7 +791,7 @@ static void insertDirectSuppression( // for (auto &x : condControlDeps) // locConsControlDeps.insert(x); } - + llvm::errs() << "fCons-no-mux = " << fCons->toString() << "\n"; if (accountMuxCondition) { Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); BoolExpression *selectOperandCondition = @@ -808,11 +808,15 @@ static void insertDirectSuppression( // producer is not control dependent from the block which produces the // condition of the mux if (!bi.isLess(muxConditionBlock, producerBlock)) { - if (consumer->getOperand(1) == connection) + if (consumer->getOperand(1) == connection) { + llvm::errs() << "MuxCondN = " << (selectOperandCondition->boolNegate())->toString() << "\n"; fCons = BoolExpression::boolAnd(fCons, - selectOperandCondition->boolNegate()); - else + selectOperandCondition); + } + else { + llvm::errs() << "MuxCond = " << selectOperandCondition->toString() << "\n"; fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); + } } } From d25207eecde158f06d048507b73db878bcbe4ce2 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Tue, 21 Oct 2025 03:24:31 +0200 Subject: [PATCH 03/15] Update --- .../lib/Support/FtdImplementation.cpp | 413 ++++++++++++++---- .../Analysis/ControlDependenceAnalysis.h | 7 - lib/Analysis/ControlDependenceAnalysis.cpp | 258 +---------- 3 files changed, 351 insertions(+), 327 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index b8d3f2049b..b81acd8e82 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -655,6 +655,167 @@ static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, return muxOp.getResult(); } +struct LocalCFG { + Region *region = nullptr; + DenseMap origMap; + Block *newProd = nullptr; + Block *newCons = nullptr; + Block *secondVisitBB = nullptr; + Block *sinkBB = nullptr; + SmallVector topoOrder; + Operation *containerOp = nullptr; + + ~LocalCFG() = default; +}; + +static std::unique_ptr +buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons) { + auto L = std::make_unique(); + Location loc = builder.getUnknownLoc(); + + // Create a temporary container for the region. + // MLIR regions must be attached to an operation. + // A dummy function is created to keep the local CFG syntactically valid. + OpBuilder::InsertionGuard guard(builder); + auto funcType = builder.getFunctionType({}, {}); + auto dummyFunc = + builder.create(loc, "__ftd_local_cfg__", funcType); + Region &R = dummyFunc.getBody(); + L->region = &R; + L->containerOp = dummyFunc; + + // Initialize entry and sink blocks. + // The entry represents the producer. + // The sink is a unified exit block for all terminal paths. + Block *entry = new Block(); + L->region->push_back(entry); + L->newProd = entry; + L->origMap[entry] = origProd; + + L->sinkBB = new Block(); + L->region->push_back(L->sinkBB); + L->origMap[L->sinkBB] = nullptr; + + // Build the reachable subgraph using DFS. + // Each original block is cloned once. + // Edges returning to the producer are redirected to a "secondVisit" node. + DenseMap cloned; + DenseSet visited; + cloned[origProd] = entry; + + std::function dfs = [&](Block *orig) { + Block *clone = cloned[orig]; + visited.insert(orig); + + auto *term = orig->getTerminator(); + SmallVector succs; + if (term) + for (auto it = term->successor_begin(), e = term->successor_end(); + it != e; ++it) + succs.push_back(*it); + + SmallVector newSuccs; + + // No successors, or the block is the consumer (different from the producer) + // In such cases, connect the block directly to the sink. + if (succs.empty() || (orig == origCons && orig != origProd)) { + builder.setInsertionPointToEnd(clone); + builder.create(loc, L->sinkBB); + return; + } + + for (Block *v : succs) { + // Successor revisits the producer + // Redirect this edge to a single secondVisitBB, which will later branch + // to the sink. + if (v == origProd) { + if (!L->secondVisitBB) { + L->secondVisitBB = new Block(); + L->region->push_back(L->secondVisitBB); + L->origMap[L->secondVisitBB] = nullptr; + } + newSuccs.push_back(L->secondVisitBB); + continue; + } + + // Regular successor + // Clone it if not already cloned. + Block *newSucc = nullptr; + if (!cloned.count(v)) { + newSucc = new Block(); + L->region->push_back(newSucc); + cloned[v] = newSucc; + L->origMap[newSucc] = v; + } else { + newSucc = cloned[v]; + } + + newSuccs.push_back(newSucc); + + if (!visited.contains(v)) + dfs(v); + } + + // Create a terminator in the cloned block that reflects its control-flow + // structure. + builder.setInsertionPointToEnd(clone); + if (newSuccs.size() == 1) { + builder.create(loc, newSuccs[0]); + } else if (newSuccs.size() == 2) { + // Use a dummy constant as a condition for a two-way branch. + Value cond = builder.create(loc, 1, 1); + builder.create(loc, cond, newSuccs[0], newSuccs[1]); + } else { + // For nodes with more than two successors, connect to the sink by + // default. + builder.create(loc, L->sinkBB); + } + }; + + dfs(origProd); + + // Connect the secondVisitBB (if any) to the sink. + if (L->secondVisitBB) { + builder.setInsertionPointToEnd(L->secondVisitBB); + builder.create(loc, L->sinkBB); + } + + // Add a return terminator to the sink block. + // This ensures the region has a valid exit. + builder.setInsertionPointToEnd(L->sinkBB); + builder.create(loc); + + // Identify the cloned consumer block in the new CFG. + if (origCons == origProd) + L->newCons = L->secondVisitBB; + else if (cloned.count(origCons)) + L->newCons = cloned[origCons]; + else + L->newCons = L->newProd; + + // Compute topological order of the local CFG. + // A post-order DFS ensures successors are visited before predecessors, + // which is required for deterministic Boolean or BDD analysis. + visited.clear(); + SmallVector order; + std::function topo = [&](Block *u) { + if (!u || visited.contains(u)) + return; + visited.insert(u); + if (auto *term = u->getTerminator()) + for (auto it = term->successor_begin(), e = term->successor_end(); + it != e; ++it) + topo(*it); + order.push_back(u); + }; + + topo(entry); + std::reverse(order.begin(), order.end()); + L->topoOrder = std::move(order); + + return L; +} + /// Apply the algorithm from FPL'22 to handle a non-loop situation of /// producer and consumer static void insertDirectSuppression( @@ -667,6 +828,15 @@ static void insertDirectSuppression( Block *consumerBlock = consumer->getBlock(); Value muxCondition = nullptr; + std::string funcName = funcOp.getName().str(); + std::string dir = "/home/yuaqin/dynamatic-scripts/TempOutputs/"; + std::string cfgFile = dir + funcName + "_localcfg.txt"; + std::string logFile = dir + funcName + "_debuglog.txt"; + std::error_code EC_log; + llvm::raw_fd_ostream log(logFile, EC_log, + static_cast(0x0004)); + llvm::raw_ostream &out = EC_log ? llvm::errs() : log; + // Account for the condition of a Mux only if it corresponds to a GAMMA GSA // gate and the producer is one of its data inputs bool accountMuxCondition = llvm::isa(consumer) && @@ -681,18 +851,18 @@ static void insertDirectSuppression( // Get the control dependencies from the consumer DenseSet consControlDeps = cdAnalysis[consumer->getBlock()].forwardControlDeps; - - llvm::errs() << "[FTD] Producer block: "; + + out << "[FTD] Producer block: "; if (producerBlock) - producerBlock->printAsOperand(llvm::errs()); + producerBlock->printAsOperand(out); else - llvm::errs() << "(null)"; - llvm::errs() << ", Consumer block: "; + out << "(null)"; + out << ", Consumer block: "; if (consumerBlock) - consumerBlock->printAsOperand(llvm::errs()); + consumerBlock->printAsOperand(out); else - llvm::errs() << "(null)"; - llvm::errs() << "\n"; + out << "(null)"; + out << "\n"; // Debug: dump consumer block control deps { Block *consumerBlock = consumer->getBlock(); @@ -701,18 +871,18 @@ static void insertDirectSuppression( auto printBlockSet = [&](llvm::StringRef label, const DenseSet &S) { - llvm::errs() << label << " = { "; + out << label << " = { "; bool first = true; for (Block *b : S) { if (!first) - llvm::errs() << ", "; + out << ", "; if (b) - b->printAsOperand(llvm::errs()); + b->printAsOperand(out); else - llvm::errs() << ""; + out << ""; first = false; } - llvm::errs() << " }\n"; + out << " }\n"; }; printBlockSet("[FTD] prod forwardControlDeps", prodEntry.forwardControlDeps); @@ -735,30 +905,118 @@ static void insertDirectSuppression( // Get rid of common entries in the two sets eliminateCommonBlocks(prodControlDeps, consControlDeps); - - DenseSet locConsControlDeps = - getLocalConsDependence(producerBlock, consumerBlock); // Compute the activation function of producer and consumer BoolExpression *fProd = enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); + if (fProd->type == experimental::boolean::ExpressionType::Zero) + return; + + auto L = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); + ControlDependenceAnalysis locCDA(*L->region); + DenseSet locConsControlDepsTmp = + *locCDA.getBlockForwardControlDeps(L->newCons); + + DenseSet locConsControlDeps; + for (Block *nb : locConsControlDepsTmp) { + Block *orig = L->origMap.lookup(nb); + if (orig) + locConsControlDeps.insert(orig); + } + BoolExpression *fCons = enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps); + // Debug: append all LocalCFGs to one txt file (no console output) + { + std::error_code EC; + llvm::raw_fd_ostream file(cfgFile, EC, + static_cast(0x0004)); + + if (!EC) { + file << "=============================================================\n"; + file << "Function: " << funcOp.getName() << "\n"; + file << "Producer: "; + if (producerBlock) + producerBlock->printAsOperand(file); + else + file << ""; + file << "\nConsumer: "; + if (consumerBlock) + consumerBlock->printAsOperand(file); + else + file << ""; + file << "\n"; + + file << "newProd: "; + if (L->newProd) + L->newProd->printAsOperand(file); + else + file << ""; + file << "\nnewCons: "; + if (L->newCons) + L->newCons->printAsOperand(file); + else + file << ""; + file << "\n"; + + file << "\nBlocks (origMap):\n"; + for (Block &b : L->region->getBlocks()) { + file << " new="; + b.printAsOperand(file); + Block *orig = L->origMap.lookup(&b); + file << " -> orig="; + if (orig) + orig->printAsOperand(file); + else + file << ""; + file << "\n"; + } + + file << "\nEdges:\n"; + for (Block &b : L->region->getBlocks()) { + file << " "; + b.printAsOperand(file); + file << " -> { "; + if (auto *term = b.getTerminator()) { + bool first = true; + for (auto it = term->successor_begin(), e = term->successor_end(); + it != e; ++it) { + if (!first) + file << ", "; + (*it)->printAsOperand(file); + first = false; + } + } + file << " }\n"; + } + + file << "\nTopoOrder:\n "; + for (Block *b : L->topoOrder) { + if (b) + b->printAsOperand(file); + else + file << ""; + file << ' '; + } + file << "\n\n"; + } + } + { auto printBlockSet = [&](llvm::StringRef label, const DenseSet &S) { - llvm::errs() << label << " = { "; + out << label << " = { "; bool first = true; for (Block *b : S) { if (!first) - llvm::errs() << ", "; + out << ", "; if (b) - b->printAsOperand(llvm::errs()); + b->printAsOperand(out); else - llvm::errs() << ""; + out << ""; first = false; } - llvm::errs() << " }\n"; + out << " }\n"; }; printBlockSet("[FTD] locConsControlDeps", locConsControlDeps); @@ -771,103 +1029,83 @@ static void insertDirectSuppression( cdAnalysis[muxConditionBlock].forwardControlDeps; { auto printBlockSet = [&](llvm::StringRef label, - const DenseSet &S) { - llvm::errs() << label << " = { "; + const DenseSet &S) { + out << label << " = { "; bool first = true; for (Block *b : S) { if (!first) - llvm::errs() << ", "; + out << ", "; if (b) - b->printAsOperand(llvm::errs()); + b->printAsOperand(out); else - llvm::errs() << ""; + out << ""; first = false; } - llvm::errs() << " }\n"; + out << " }\n"; }; printBlockSet("[FTD] muxControlDeps", condControlDeps); } - // for (auto &x : condControlDeps) - // locConsControlDeps.insert(x); } - llvm::errs() << "fCons-no-mux = " << fCons->toString() << "\n"; + out << "fCons-no-mux = " << fCons->toString() << "\n"; if (accountMuxCondition) { Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); BoolExpression *selectOperandCondition = BoolExpression::parseSop(bi.getBlockCondition(muxConditionBlock)); - llvm::errs() << "[MUX] Mux Condition Block: "; + out << "[MUX] Mux Condition Block: "; if (muxConditionBlock) - muxConditionBlock->printAsOperand(llvm::errs()); + muxConditionBlock->printAsOperand(out); else - llvm::errs() << "(null)"; - llvm::errs() << "\n"; + out << "(null)"; + out << "\n"; - // The condition must be taken into account for `fCons` only if the - // producer is not control dependent from the block which produces the - // condition of the mux if (!bi.isLess(muxConditionBlock, producerBlock)) { if (consumer->getOperand(1) == connection) { - llvm::errs() << "MuxCondN = " << (selectOperandCondition->boolNegate())->toString() << "\n"; - fCons = BoolExpression::boolAnd(fCons, - selectOperandCondition); - } - else { - llvm::errs() << "MuxCond = " << selectOperandCondition->toString() << "\n"; + out << "MuxCondN = " + << (selectOperandCondition->boolNegate())->toString() << "\n"; + fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); + } else { + out << "MuxCond = " << selectOperandCondition->toString() << "\n"; fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); } } } - if (llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - (llvm::isa(consumer) && - !llvm::isa(consumer)) || - (llvm::isa(consumer) && - !llvm::isa(consumer))) - return; - - /// f_supp = f_prod and not f_cons - llvm::errs() << "fProd = " << fProd->toString() << "\n"; - llvm::errs() << "fCons = " << fCons->toString() << "\n"; - if (fProd->type == experimental::boolean::ExpressionType::Zero) - return; + // f_supp = f_prod and not f_cons + out << "fProd = " << fProd->toString() << "\n"; + out << "fCons = " << fCons->toString() << "\n"; BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); - llvm::errs() << "fSupmin = " << fSup->toString() << "\n"; + out << "fSupmin = " << fSup->toString() << "\n"; if (fProd->type == experimental::boolean::ExpressionType::Zero) { - llvm::errs() << "[FTD] fProd == 0 detected\n"; - llvm::errs() << " producer block: "; + out << "[FTD] fProd == 0 detected\n"; + out << " producer block: "; if (producerBlock) - producerBlock->printAsOperand(llvm::errs()); + producerBlock->printAsOperand(out); else - llvm::errs() << ""; - llvm::errs() << "\n"; + out << ""; + out << "\n"; - llvm::errs() << " consumer block: "; + out << " consumer block: "; if (consumerBlock) - consumerBlock->printAsOperand(llvm::errs()); + consumerBlock->printAsOperand(out); else - llvm::errs() << ""; - llvm::errs() << "\n"; + out << ""; + out << "\n"; - llvm::errs() << " producer (connection): "; + out << " producer (connection): "; if (Operation *def = connection.getDefiningOp()) { - llvm::errs() << def->getName() << " @ "; - def->getLoc().print(llvm::errs()); + out << def->getName() << " @ "; + def->getLoc().print(out); } else { - llvm::errs() << ""; + out << ""; } - llvm::errs() << "\n"; + out << "\n"; - llvm::errs() << " consumer: " << consumer->getName() << " @ "; - consumer->getLoc().print(llvm::errs()); - llvm::errs() << "\n"; + out << " consumer: " << consumer->getName() << " @ "; + consumer->getLoc().print(out); + out << "\n"; } // If the activation function is not zero, then a suppress block is to be @@ -875,7 +1113,23 @@ static void insertDirectSuppression( if (fSup->type != experimental::boolean::ExpressionType::Zero) { std::set blocks = fSup->getVariables(); - std::vector cofactorList(blocks.begin(), blocks.end()); + DenseMap rank; + unsigned i = 0; + for (Block *b : L->topoOrder) + if (auto *ob = L->origMap.lookup(b)) + rank[ob] = i++; + + std::vector cofactorList; + cofactorList.reserve(blocks.size()); + std::vector> tmp; + for (auto &var : blocks) + if (auto blkOpt = bi.getBlockFromCondition(var)) + if (rank.count(*blkOpt)) + tmp.emplace_back(rank[*blkOpt], var); + llvm::sort(tmp, [](auto &a, auto &b) { return a.first < b.first; }); + for (auto &p : tmp) + cofactorList.push_back(p.second); + BDD *bdd = buildBDD(fSup, cofactorList); Value branchCond = bddToCircuit(rewriter, bdd, consumer->getBlock(), bi); @@ -896,6 +1150,7 @@ static void insertDirectSuppression( use.set(branchOp.getFalseResult()); } } + rewriter.eraseOp(L->containerOp); } void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, diff --git a/include/dynamatic/Analysis/ControlDependenceAnalysis.h b/include/dynamatic/Analysis/ControlDependenceAnalysis.h index 3a9560b92d..8fe762aebc 100644 --- a/include/dynamatic/Analysis/ControlDependenceAnalysis.h +++ b/include/dynamatic/Analysis/ControlDependenceAnalysis.h @@ -76,13 +76,6 @@ class ControlDependenceAnalysis { void addDepsOfDeps(Region ®ion); }; -/// Build a local CDG on the subgraph rooted at `prod` with a single sink node -/// Sk. Paths stop when reaching: a real end, the second visit to `prod`, or -/// `cons`. If `prod == cons`, the very first hit at the start does not stop. -/// The function returns the set of all control-ancestors of `cons` in this -/// local CDG. -DenseSet getLocalConsDependence(Block *prod, Block *cons); - } // namespace dynamatic #endif // DYNAMATIC_ANALYSIS_CONTROLDEPENDENCEANALYSIS_H diff --git a/lib/Analysis/ControlDependenceAnalysis.cpp b/lib/Analysis/ControlDependenceAnalysis.cpp index 5d564553aa..aad6f315c2 100644 --- a/lib/Analysis/ControlDependenceAnalysis.cpp +++ b/lib/Analysis/ControlDependenceAnalysis.cpp @@ -159,14 +159,23 @@ void dynamatic::ControlDependenceAnalysis::identifyAllControlDeps( void dynamatic::ControlDependenceAnalysis::addDepsOfDeps(Region ®ion) { - // For each block, consider each of its dependencies (`oneDep`) and move each - // of its dependencies into block's - for (Block &block : region.getBlocks()) { - BlockControlDeps blockControlDeps = blocksControlDeps[&block]; - for (auto &oneDep : blockControlDeps.allControlDeps) { - DenseSet &oneDepDeps = blocksControlDeps[oneDep].allControlDeps; - for (auto &oneDepDep : oneDepDeps) - blocksControlDeps[&block].allControlDeps.insert(oneDepDep); + bool changed = true; + while (changed) { + changed = false; + + // For each block, consider each of its dependencies and move each + // of its dependencies into block's + for (Block &block : region.getBlocks()) { + DenseSet &blockDeps = blocksControlDeps[&block].allControlDeps; + SmallVector currentDeps(blockDeps.begin(), blockDeps.end()); + + for (Block *oneDep : currentDeps) { + DenseSet &oneDepDeps = blocksControlDeps[oneDep].allControlDeps; + for (Block *oneDepDep : oneDepDeps) { + if (blockDeps.insert(oneDepDep).second) + changed = true; // Found a new dependency + } + } } } } @@ -226,239 +235,6 @@ dynamatic::ControlDependenceAnalysis::getAllBlockDeps() const { return blocksControlDeps; } -DenseSet dynamatic::getLocalConsDependence(Block *prod, Block *cons) { - if (!prod || !cons) - return DenseSet{}; - Region *reg = prod->getParent(); - if (!reg || reg != cons->getParent()) - return DenseSet{}; - - // 1) Build the local subgraph G' with a single sink node Sk - struct NodeInfo { - SmallVector succs; // Successors in G' (excluding Sk) - bool toSink = false; // Whether this node has an edge to Sk - }; - - DenseMap G; // Only contains blocks in the local subgraph - Block *SINK = nullptr; // Use nullptr to represent Sk (the unique exit) - - enum class Mark : int { kUnseen = 0, kSeen, kDone }; - DenseMap mark; - - // Construction rules: - // 1) Keep all edges u -> cons. The block `cons` itself is not expanded, - // but must always have an outgoing edge cons -> Sk. - // 2) For any successor s == prod: - // - If prod == cons, connect u -> cons (and cons will later connect to - // Sk). - // - If prod != cons, connect u -> Sk (this represents the second visit - // to prod). - // 3) For any block with no successors or successors outside the current - // region, - // connect that block -> Sk. - // 4) If prod == cons and b == cons at the starting point (isStart == true), - // do NOT stop; expand successors once normally. - std::function build = [&](Block *b, bool isStart) { - (void)G[b]; // Ensure the node exists in the map - - // Case: current block is `cons` - // Always add an edge cons -> Sk - if (b == cons) { - G[b].toSink = true; // cons -> Sk always exists - // At the starting node (prod == cons, first visit): keep exploring - if (!(isStart && prod == cons)) { - // For non-start or when prod != cons, stop expanding at cons - mark[b] = Mark::kDone; - return; - } - // Otherwise continue expanding successors normally - } - - if (mark[b] == Mark::kDone) - return; - if (mark[b] == Mark::kSeen && !isStart) - return; - mark[b] = Mark::kSeen; - - auto succRange = b->getSuccessors(); - if (succRange.empty()) { - // True end block: connect to Sk - G[b].toSink = true; - mark[b] = Mark::kDone; - return; - } - - for (Block *s : succRange) { - // Out-of-region successors are treated as -> Sk - if (!s || s->getParent() != reg) { - G[b].toSink = true; - continue; - } - - // Successor is `prod` - if (s == prod) { - if (prod == cons) { - // Second visit to prod is treated as reaching cons. - // First connect to cons, then cons -> Sk will terminate the path. - (void)G[cons]; - G[b].succs.push_back(cons); - } else { - // prod != cons: second visit to prod terminates directly -> Sk - G[b].toSink = true; - } - continue; - } - - // Successor is `cons`: keep the edge but do not expand cons - if (s == cons) { - (void)G[cons]; - G[b].succs.push_back(cons); - continue; // Do not recursively expand cons - } - - // Normal edge b -> s - G[b].succs.push_back(s); - build(s, /*isStart=*/false); - } - - mark[b] = Mark::kDone; - }; - - build(prod, /*isStart=*/true); - - // If cons is unreachable in the local subgraph, return an empty set - if (!G.contains(cons)) - return DenseSet{}; - - // Expand all "toSink" edges into explicit -> Sk edges - SmallVector nodes; - nodes.reserve(G.size() + 1); - for (auto &kv : G) - nodes.push_back(kv.first); - nodes.push_back(SINK); - - DenseMap> succP, predP; - for (Block *n : nodes) { - succP[n]; - predP[n]; - } - for (auto &kv : G) { - Block *u = kv.first; - for (Block *v : kv.second.succs) { - succP[u].push_back(v); - predP[v].push_back(u); - } - if (kv.second.toSink) { - succP[u].push_back(SINK); - predP[SINK].push_back(u); - } - } - - // 2) Compute post-dominance sets with respect to Sk - DenseMap> postdom; - for (Block *n : nodes) { - if (n == SINK) { - postdom[n].insert(SINK); - } else { - for (Block *m : nodes) - postdom[n].insert(m); - } - } - - bool changed = true; - while (changed) { - changed = false; - for (Block *n : nodes) { - if (n == SINK) - continue; - - // inter = intersection of postdom(s) for all successors s of n - DenseSet inter; - bool first = true; - for (Block *s : succP[n]) { - if (first) { - inter = postdom[s]; - first = false; - } else { - DenseSet tmp; - for (Block *x : inter) - if (postdom[s].contains(x)) - tmp.insert(x); - inter.swap(tmp); - } - } - - DenseSet newSet = inter; - newSet.insert(n); - - // Check for changes - if (newSet.size() != postdom[n].size()) { - postdom[n].swap(newSet); - changed = true; - continue; - } - for (Block *x : newSet) { - if (!postdom[n].contains(x)) { - postdom[n].swap(newSet); - changed = true; - break; - } - } - } - } - - auto postDominates = [&](Block *x, Block *y) -> bool { - auto it = postdom.find(y); - return it != postdom.end() && it->second.contains(x); - }; - - // 3) Query CDG direct predecessors (without explicitly building CDG) - // Ferrante–Ottenstein–Warren criterion: - // ∃ Y->Z such that X ∈ postdom(Z) and X ∉ postdom(Y) - // -> add edge Y -> X in CDG - auto predsInCDG = [&](Block *X, DenseSet &out) { - for (Block *Y : nodes) { - if (Y == SINK) - continue; - if (postDominates(X, Y)) - continue; - for (Block *Z : succP[Y]) { - if (postDominates(X, Z)) { - out.insert(Y); - break; - } - } - } - }; - - // 4) Reverse closure: collect all control ancestors of `cons` - DenseSet upstream, frontier; - SmallVector stack; - - // Direct control predecessors of cons - predsInCDG(cons, frontier); - for (Block *p : frontier) { - upstream.insert(p); - stack.push_back(p); - } - - // BFS/DFS over CDG to get transitive closure (all upstream control points) - while (!stack.empty()) { - Block *x = stack.back(); - stack.pop_back(); - DenseSet parents; - predsInCDG(x, parents); - for (Block *p : parents) { - if (!upstream.contains(p)) { - upstream.insert(p); - stack.push_back(p); - } - } - } - - return upstream; -} - void dynamatic::ControlDependenceAnalysis::printAllBlocksDeps() const { DEBUG_WITH_TYPE( From 34dd7ea9997df3fdbd012d87527ae1c213a14d19 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Tue, 21 Oct 2025 03:25:56 +0200 Subject: [PATCH 04/15] small --- lib/Analysis/ControlDependenceAnalysis.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/Analysis/ControlDependenceAnalysis.cpp b/lib/Analysis/ControlDependenceAnalysis.cpp index aad6f315c2..86b12e78bb 100644 --- a/lib/Analysis/ControlDependenceAnalysis.cpp +++ b/lib/Analysis/ControlDependenceAnalysis.cpp @@ -240,8 +240,7 @@ void dynamatic::ControlDependenceAnalysis::printAllBlocksDeps() const { DEBUG_WITH_TYPE( "CONTROL_DEPENDENCY_ANALYSIS", llvm::dbgs() << "\n*********************************\n\n"; - for (auto &elem - : blocksControlDeps) { + for (auto &elem : blocksControlDeps) { Block *block = elem.first; block->printAsOperand(llvm::dbgs()); llvm::dbgs() << " is control dependent on: "; From d3f5cab394d03896c421a61de8a89d8e707b1255 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Tue, 21 Oct 2025 03:55:32 +0200 Subject: [PATCH 05/15] Weird, why we still need this --- experimental/lib/Support/FtdImplementation.cpp | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index b81acd8e82..a2ae09017a 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -910,7 +910,17 @@ static void insertDirectSuppression( enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); if (fProd->type == experimental::boolean::ExpressionType::Zero) return; - + if (llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + (llvm::isa(consumer) && + !llvm::isa(consumer)) || + (llvm::isa(consumer) && + !llvm::isa(consumer))) + return; auto L = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); ControlDependenceAnalysis locCDA(*L->region); DenseSet locConsControlDepsTmp = From 032216111e180fc2729ee40b844405e19a929a60 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Tue, 21 Oct 2025 04:16:03 +0200 Subject: [PATCH 06/15] add comments --- .../lib/Support/FtdImplementation.cpp | 29 +++++++++++++------ 1 file changed, 20 insertions(+), 9 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index a2ae09017a..aae4b85e10 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -656,18 +656,29 @@ static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, } struct LocalCFG { + // The MLIR region representing the local subgraph. Region *region = nullptr; + // Mapping: block in local graph -> original block. DenseMap origMap; + // The producer block in the local CFG. Block *newProd = nullptr; + // The consumer block in the local CFG. Block *newCons = nullptr; + // A replicated block used when the producer is revisited (for loops). Block *secondVisitBB = nullptr; + // A unique sink (exit) block to which all terminal paths lead. Block *sinkBB = nullptr; + // Topological order of the reconstructed region. SmallVector topoOrder; + // Temporary parent operation that owns the region. Operation *containerOp = nullptr; ~LocalCFG() = default; }; +/// Build a local control-flow subgraph (LocalCFG) between a producer and +/// consumer. The subgraph is reconstructed as a region with unique entry +/// (producer) and exit (sink). static std::unique_ptr buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons) { auto L = std::make_unique(); @@ -911,15 +922,15 @@ static void insertDirectSuppression( if (fProd->type == experimental::boolean::ExpressionType::Zero) return; if (llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - (llvm::isa(consumer) && - !llvm::isa(consumer)) || - (llvm::isa(consumer) && - !llvm::isa(consumer))) + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + llvm::isa_and_nonnull(consumer) || + (llvm::isa(consumer) && + !llvm::isa(consumer)) || + (llvm::isa(consumer) && + !llvm::isa(consumer))) return; auto L = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); ControlDependenceAnalysis locCDA(*L->region); From 4ed8487fc3521848778fbcddbb0dacafc9e1798a Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Tue, 21 Oct 2025 23:59:58 +0200 Subject: [PATCH 07/15] update --- .../lib/Support/FtdImplementation.cpp | 49 +++++++++---------- 1 file changed, 22 insertions(+), 27 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index aae4b85e10..eef62cff12 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -921,25 +921,15 @@ static void insertDirectSuppression( enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); if (fProd->type == experimental::boolean::ExpressionType::Zero) return; - if (llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - llvm::isa_and_nonnull(consumer) || - (llvm::isa(consumer) && - !llvm::isa(consumer)) || - (llvm::isa(consumer) && - !llvm::isa(consumer))) - return; - auto L = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); - ControlDependenceAnalysis locCDA(*L->region); - DenseSet locConsControlDepsTmp = - *locCDA.getBlockForwardControlDeps(L->newCons); + auto locGraph = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); + ControlDependenceAnalysis locCDA(*locGraph->region); + DenseSet locConsControlDepsTmp = + locCDA.getAllBlockDeps()[locGraph->newCons].allControlDeps; + DenseSet locConsControlDeps; for (Block *nb : locConsControlDepsTmp) { - Block *orig = L->origMap.lookup(nb); + Block *orig = locGraph->origMap.lookup(nb); if (orig) locConsControlDeps.insert(orig); } @@ -969,22 +959,26 @@ static void insertDirectSuppression( file << "\n"; file << "newProd: "; - if (L->newProd) - L->newProd->printAsOperand(file); + if (locGraph->newProd) + locGraph->newProd->printAsOperand(file); else file << ""; file << "\nnewCons: "; - if (L->newCons) - L->newCons->printAsOperand(file); + if (locGraph->newCons) + locGraph->newCons->printAsOperand(file); else file << ""; file << "\n"; + file << "regionFront = "; + locGraph->region->front().printAsOperand(file); + file << "\n"; + file << "\nBlocks (origMap):\n"; - for (Block &b : L->region->getBlocks()) { + for (Block &b : locGraph->region->getBlocks()) { file << " new="; b.printAsOperand(file); - Block *orig = L->origMap.lookup(&b); + Block *orig = locGraph->origMap.lookup(&b); file << " -> orig="; if (orig) orig->printAsOperand(file); @@ -994,7 +988,7 @@ static void insertDirectSuppression( } file << "\nEdges:\n"; - for (Block &b : L->region->getBlocks()) { + for (Block &b : locGraph->region->getBlocks()) { file << " "; b.printAsOperand(file); file << " -> { "; @@ -1012,7 +1006,7 @@ static void insertDirectSuppression( } file << "\nTopoOrder:\n "; - for (Block *b : L->topoOrder) { + for (Block *b : locGraph->topoOrder) { if (b) b->printAsOperand(file); else @@ -1040,6 +1034,7 @@ static void insertDirectSuppression( out << " }\n"; }; + printBlockSet("[FTD] locConsControlDepsTmp", locConsControlDepsTmp); printBlockSet("[FTD] locConsControlDeps", locConsControlDeps); } @@ -1136,8 +1131,8 @@ static void insertDirectSuppression( DenseMap rank; unsigned i = 0; - for (Block *b : L->topoOrder) - if (auto *ob = L->origMap.lookup(b)) + for (Block *b : locGraph->topoOrder) + if (auto *ob = locGraph->origMap.lookup(b)) rank[ob] = i++; std::vector cofactorList; @@ -1171,7 +1166,7 @@ static void insertDirectSuppression( use.set(branchOp.getFalseResult()); } } - rewriter.eraseOp(L->containerOp); + rewriter.eraseOp(locGraph->containerOp); } void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, From 5f774c41d7aaa2c35fa9bdaf8106f976a65b33c3 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Wed, 22 Oct 2025 00:19:38 +0200 Subject: [PATCH 08/15] small fix --- experimental/lib/Support/FtdImplementation.cpp | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index eef62cff12..ac05726888 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -1237,11 +1237,10 @@ void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, !llvm::isa(consumerOp)) || llvm::isa(operand.getType())) return; + // Handle the suppression in all the other cases (including the operand being + // a function argument) + insertDirectSuppression(rewriter, funcOp, consumerOp, operand, bi, cda); } - - // Handle the suppression in all the other cases (including the operand being - // a function argument) - insertDirectSuppression(rewriter, funcOp, consumerOp, operand, bi, cda); } void ftd::addSupp(handshake::FuncOp &funcOp, PatternRewriter &rewriter) { From 1b9af8812de6f402380c3fcae19406d0faacf547 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Wed, 22 Oct 2025 00:45:05 +0200 Subject: [PATCH 09/15] update --- experimental/lib/Support/FtdImplementation.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index ac05726888..793dfb643d 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -1145,7 +1145,12 @@ static void insertDirectSuppression( llvm::sort(tmp, [](auto &a, auto &b) { return a.first < b.first; }); for (auto &p : tmp) cofactorList.push_back(p.second); - + + llvm::errs() << "[CofactorList] "; + for (const auto &s : cofactorList) + llvm::errs() << s << " "; + llvm::errs() << "\n"; + BDD *bdd = buildBDD(fSup, cofactorList); Value branchCond = bddToCircuit(rewriter, bdd, consumer->getBlock(), bi); From 09df431a3a7ced7159149ba2244ced5067d4819b Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sat, 6 Dec 2025 19:57:36 +0100 Subject: [PATCH 10/15] Temp Update --- .../lib/Support/FtdImplementation.cpp | 82 ++++++++++++++++--- experimental/lib/Support/FtdSupport.cpp | 2 +- 2 files changed, 72 insertions(+), 12 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index 793dfb643d..b4e5edc10c 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -167,19 +167,34 @@ static void eliminateCommonBlocks(DenseSet &s1, /// the set; the product of all such paths are added". static BoolExpression *enumeratePaths(Block *start, Block *end, const ftd::BlockIndexing &bi, - const DenseSet &controlDeps) { + const DenseSet &controlDeps, + ArrayRef toAvoid = {}) { // Start with a boolean expression of zero (so that new conditions can be // added) BoolExpression *sop = BoolExpression::boolZero(); // Find all the paths from the producer to the consumer, using a DFS - std::vector> allPaths = findAllPaths(start, end, bi); + std::vector> allPaths = + findAllPaths(start, end, bi, nullptr, toAvoid); // If the start and end block are the same (e.g., BB0 to BB0) and there is no // real path between them, then consider the sop = 1 if (start == end && allPaths.size() == 0) sop = BoolExpression::boolOne(); - + if (start == end) { + llvm::errs() << "Start == End: "; + start->printAsOperand(llvm::errs()); + llvm::errs() << "\nAll paths:\n"; + + for (const auto &path : allPaths) { + llvm::errs() << " Path: "; + for (Block *bb : path) { + bb->printAsOperand(llvm::errs()); + llvm::errs() << " "; + } + llvm::errs() << "\n"; + } + } // For each path for (const std::vector &path : allPaths) { @@ -680,7 +695,8 @@ struct LocalCFG { /// consumer. The subgraph is reconstructed as a region with unique entry /// (producer) and exit (sink). static std::unique_ptr -buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons) { +buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, + const ftd::BlockIndexing &bi) { auto L = std::make_unique(); Location loc = builder.getUnknownLoc(); @@ -749,6 +765,12 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons) { continue; } + // Stop traversal after encountering a block before producer. + if (bi.isLess(v, origProd) && v != origCons) { + newSuccs.push_back(L->sinkBB); + continue; + } + // Regular successor // Clone it if not already cloned. Block *newSucc = nullptr; @@ -855,6 +877,12 @@ static void insertDirectSuppression( (consumer->getOperand(1) == connection || consumer->getOperand(2) == connection); + bool accountMuCondition = llvm::isa(consumer) && + consumer->hasAttr(FTD_EXPLICIT_MU) && + (consumer->getOperand(1) == connection || + consumer->getOperand(2) == connection); + if (accountMuCondition) + out << "[FTD] Accounting for MU condition\n"; // Get the control dependencies from the producer DenseSet prodControlDeps = cdAnalysis[producerBlock].forwardControlDeps; @@ -922,11 +950,41 @@ static void insertDirectSuppression( if (fProd->type == experimental::boolean::ExpressionType::Zero) return; - auto locGraph = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock); + Block *anotherMuInputBlock = nullptr; + if (accountMuCondition) { + // Pick the other data input of the MU mux + Value anotherInput = (consumer->getOperand(1) == connection) + ? consumer->getOperand(2) + : consumer->getOperand(1); + + Operation *owner = anotherInput.getDefiningOp(); + + // Traverse upwards while the current owner is a conditional branch + while (auto cbr = llvm::dyn_cast_or_null(owner)) { + Value dataInput = cbr->getOperand(1); + owner = dataInput.getDefiningOp(); + } + + if (owner) + anotherMuInputBlock = owner->getBlock(); + else + llvm::report_fatal_error("MU input value has no defining operation."); + } + + ArrayRef anotherMuInputBlocks = + anotherMuInputBlock ? ArrayRef(&anotherMuInputBlock, 1) + : ArrayRef({}); + + auto locGraph = + buildLocalCFGRegion(rewriter, producerBlock, consumerBlock, bi); ControlDependenceAnalysis locCDA(*locGraph->region); DenseSet locConsControlDepsTmp = locCDA.getAllBlockDeps()[locGraph->newCons].allControlDeps; - + + out << "[FTD] Avoid Block: "; + if (anotherMuInputBlock) + anotherMuInputBlock->printAsOperand(out); + out << "\n"; DenseSet locConsControlDeps; for (Block *nb : locConsControlDepsTmp) { Block *orig = locGraph->origMap.lookup(nb); @@ -935,7 +993,8 @@ static void insertDirectSuppression( } BoolExpression *fCons = - enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps); + enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps, + anotherMuInputBlocks); // Debug: append all LocalCFGs to one txt file (no console output) { @@ -1145,12 +1204,12 @@ static void insertDirectSuppression( llvm::sort(tmp, [](auto &a, auto &b) { return a.first < b.first; }); for (auto &p : tmp) cofactorList.push_back(p.second); - + llvm::errs() << "[CofactorList] "; for (const auto &s : cofactorList) llvm::errs() << s << " "; llvm::errs() << "\n"; - + BDD *bdd = buildBDD(fSup, cofactorList); Value branchCond = bddToCircuit(rewriter, bdd, consumer->getBlock(), bi); @@ -1242,8 +1301,9 @@ void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, !llvm::isa(consumerOp)) || llvm::isa(operand.getType())) return; - // Handle the suppression in all the other cases (including the operand being - // a function argument) + + // Handle the suppression in all the other cases (including the operand + // being a function argument) insertDirectSuppression(rewriter, funcOp, consumerOp, operand, bi, cda); } } diff --git a/experimental/lib/Support/FtdSupport.cpp b/experimental/lib/Support/FtdSupport.cpp index 58c09fb7f4..ce800f0f4a 100644 --- a/experimental/lib/Support/FtdSupport.cpp +++ b/experimental/lib/Support/FtdSupport.cpp @@ -134,7 +134,7 @@ static void dfsAllPaths(Block *start, Block *end, std::vector &path, // Do not run DFS if the successor is in the list of blocks to traverse bool incorrectPath = false; for (auto *toAvoid : blocksToAvoid) { - if (toAvoid == successor && bi.isGreater(toAvoid, blockToTraverse)) { + if (toAvoid == successor) { incorrectPath = true; break; } From 7842b89f216044416d8ae34d3b5f6a18ef1d8814 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sun, 7 Dec 2025 00:43:14 +0100 Subject: [PATCH 11/15] temp --- .../lib/Support/FtdImplementation.cpp | 280 ++++++++---------- experimental/lib/Support/FtdSupport.cpp | 2 +- 2 files changed, 127 insertions(+), 155 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index b4e5edc10c..ad9e651d43 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -167,34 +167,19 @@ static void eliminateCommonBlocks(DenseSet &s1, /// the set; the product of all such paths are added". static BoolExpression *enumeratePaths(Block *start, Block *end, const ftd::BlockIndexing &bi, - const DenseSet &controlDeps, - ArrayRef toAvoid = {}) { + const DenseSet &controlDeps) { // Start with a boolean expression of zero (so that new conditions can be // added) BoolExpression *sop = BoolExpression::boolZero(); // Find all the paths from the producer to the consumer, using a DFS - std::vector> allPaths = - findAllPaths(start, end, bi, nullptr, toAvoid); + std::vector> allPaths = findAllPaths(start, end, bi); // If the start and end block are the same (e.g., BB0 to BB0) and there is no // real path between them, then consider the sop = 1 if (start == end && allPaths.size() == 0) sop = BoolExpression::boolOne(); - if (start == end) { - llvm::errs() << "Start == End: "; - start->printAsOperand(llvm::errs()); - llvm::errs() << "\nAll paths:\n"; - - for (const auto &path : allPaths) { - llvm::errs() << " Path: "; - for (Block *bb : path) { - bb->printAsOperand(llvm::errs()); - llvm::errs() << " "; - } - llvm::errs() << "\n"; - } - } + // For each path for (const std::vector &path : allPaths) { @@ -679,7 +664,7 @@ struct LocalCFG { Block *newProd = nullptr; // The consumer block in the local CFG. Block *newCons = nullptr; - // A replicated block used when the producer is revisited (for loops). + // A replicated block used for self-loop delivery (Producer == Consumer). Block *secondVisitBB = nullptr; // A unique sink (exit) block to which all terminal paths lead. Block *sinkBB = nullptr; @@ -700,9 +685,7 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, auto L = std::make_unique(); Location loc = builder.getUnknownLoc(); - // Create a temporary container for the region. - // MLIR regions must be attached to an operation. - // A dummy function is created to keep the local CFG syntactically valid. + // Setup Region Container OpBuilder::InsertionGuard guard(builder); auto funcType = builder.getFunctionType({}, {}); auto dummyFunc = @@ -711,141 +694,165 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, L->region = &R; L->containerOp = dummyFunc; - // Initialize entry and sink blocks. - // The entry represents the producer. - // The sink is a unified exit block for all terminal paths. + // Sink Block: The unified exit for all paths (valid or suppressed). + L->sinkBB = new Block(); + R.push_back(L->sinkBB); + L->origMap[L->sinkBB] = nullptr; + + // Producer Block: The entry point of the local CFG. Block *entry = new Block(); - L->region->push_back(entry); + R.push_back(entry); L->newProd = entry; L->origMap[entry] = origProd; - L->sinkBB = new Block(); - L->region->push_back(L->sinkBB); - L->origMap[L->sinkBB] = nullptr; - - // Build the reachable subgraph using DFS. - // Each original block is cloned once. - // Edges returning to the producer are redirected to a "secondVisit" node. DenseMap cloned; DenseSet visited; cloned[origProd] = entry; - std::function dfs = [&](Block *orig) { - Block *clone = cloned[orig]; - visited.insert(orig); + // DFS Function + std::function dfs = [&](Block *currOrig, Block *currNew) { + visited.insert(currOrig); - auto *term = orig->getTerminator(); - SmallVector succs; - if (term) - for (auto it = term->successor_begin(), e = term->successor_end(); - it != e; ++it) - succs.push_back(*it); - - SmallVector newSuccs; - - // No successors, or the block is the consumer (different from the producer) - // In such cases, connect the block directly to the sink. - if (succs.empty() || (orig == origCons && orig != origProd)) { - builder.setInsertionPointToEnd(clone); + auto *term = currOrig->getTerminator(); + + // Dead End: Implicit flow to Sink. + if (!term || term->getNumSuccessors() == 0) { + builder.setInsertionPointToEnd(currNew); builder.create(loc, L->sinkBB); return; } - for (Block *v : succs) { - // Successor revisits the producer - // Redirect this edge to a single secondVisitBB, which will later branch - // to the sink. - if (v == origProd) { - if (!L->secondVisitBB) { - L->secondVisitBB = new Block(); - L->region->push_back(L->secondVisitBB); - L->origMap[L->secondVisitBB] = nullptr; + // LIST 1: The distinct successors in the NEW Local CFG for the current block. + // Used to construct the BranchOp/CondBranchOp. + SmallVector localSuccessors; + + // LIST 2: The successors that are valid and new, requiring further DFS traversal. + // Stored as pairs: {Original Successor, New Local Block}. + SmallVector, 2> successorsToVisit; + + for (auto it = term->successor_begin(), e = term->successor_end(); it != e; ++it) { + Block *succOrig = *it; + Block *nextBlockInLocalCFG = nullptr; // Where the edge points to in the new graph + + // --- LOGIC: Determine the edge destination based on rules --- + + // Case 1: Consumer Reached (Valid Delivery) + if (succOrig == origCons) { + if (succOrig == origProd) { + // Self-loop delivery + if (!L->secondVisitBB) { + L->secondVisitBB = new Block(); + R.push_back(L->secondVisitBB); + L->origMap[L->secondVisitBB] = nullptr; + // Terminate SecondVisit immediately to Sink + OpBuilder::InsertionGuard g(builder); + builder.setInsertionPointToEnd(L->secondVisitBB); + builder.create(loc, L->sinkBB); + } + nextBlockInLocalCFG = L->secondVisitBB; + L->newCons = L->secondVisitBB; + } + else { + // Standard delivery + if (!L->newCons || L->newCons == L->secondVisitBB) { + Block *proxy = new Block(); + R.push_back(proxy); + L->origMap[proxy] = succOrig; + L->newCons = proxy; + // Terminate Proxy immediately to Sink + OpBuilder::InsertionGuard g(builder); + builder.setInsertionPointToEnd(proxy); + builder.create(loc, L->sinkBB); + } + nextBlockInLocalCFG = L->newCons; } - newSuccs.push_back(L->secondVisitBB); - continue; } - - // Stop traversal after encountering a block before producer. - if (bi.isLess(v, origProd) && v != origCons) { - newSuccs.push_back(L->sinkBB); - continue; + // Case 2: Producer revisited, consumer not reached (Invalid) + else if (succOrig == origProd) { + nextBlockInLocalCFG = L->sinkBB; } - - // Regular successor - // Clone it if not already cloned. - Block *newSucc = nullptr; - if (!cloned.count(v)) { - newSucc = new Block(); - L->region->push_back(newSucc); - cloned[v] = newSucc; - L->origMap[newSucc] = v; - } else { - newSucc = cloned[v]; + // Case 3: Visited + else if (visited.count(succOrig)) { + if (cloned.count(succOrig)) { + nextBlockInLocalCFG = cloned[succOrig]; + } else { + nextBlockInLocalCFG = L->sinkBB; + } + } + // Case 4: Invalid Back-edge + else if (bi.isLess(succOrig, currOrig)) { + nextBlockInLocalCFG = L->sinkBB; + } + // Case 5: Valid Forward Edge (Continue Traversal) + else { + Block *newSucc = new Block(); + R.push_back(newSucc); + cloned[succOrig] = newSucc; + L->origMap[newSucc] = succOrig; + + nextBlockInLocalCFG = newSucc; + // Schedule this node for DFS visitation + successorsToVisit.push_back({succOrig, newSucc}); } - newSuccs.push_back(newSucc); - - if (!visited.contains(v)) - dfs(v); + // Add the determined destination to the list of local successors + localSuccessors.push_back(nextBlockInLocalCFG); } - // Create a terminator in the cloned block that reflects its control-flow - // structure. - builder.setInsertionPointToEnd(clone); - if (newSuccs.size() == 1) { - builder.create(loc, newSuccs[0]); - } else if (newSuccs.size() == 2) { - // Use a dummy constant as a condition for a two-way branch. + // --- CONSTRUCTION: Create the branch instruction --- + + builder.setInsertionPointToEnd(currNew); + if (localSuccessors.size() == 1) { + builder.create(loc, localSuccessors[0]); + } else if (localSuccessors.size() == 2) { + // Placeholder condition for 2-way branches Value cond = builder.create(loc, 1, 1); - builder.create(loc, cond, newSuccs[0], newSuccs[1]); + builder.create(loc, cond, localSuccessors[0], localSuccessors[1]); } else { - // For nodes with more than two successors, connect to the sink by - // default. + // Default fall-through for complex control flow builder.create(loc, L->sinkBB); } - }; - dfs(origProd); + // --- TRAVERSAL: Continue DFS --- + for (auto &pair : successorsToVisit) { + dfs(pair.first, pair.second); + } + }; - // Connect the secondVisitBB (if any) to the sink. - if (L->secondVisitBB) { - builder.setInsertionPointToEnd(L->secondVisitBB); - builder.create(loc, L->sinkBB); - } + // Start DFS + dfs(origProd, L->newProd); - // Add a return terminator to the sink block. - // This ensures the region has a valid exit. + // Finalize Sink builder.setInsertionPointToEnd(L->sinkBB); builder.create(loc); - // Identify the cloned consumer block in the new CFG. - if (origCons == origProd) - L->newCons = L->secondVisitBB; - else if (cloned.count(origCons)) - L->newCons = cloned[origCons]; - else - L->newCons = L->newProd; + if (!L->newCons) L->newCons = L->sinkBB; - // Compute topological order of the local CFG. - // A post-order DFS ensures successors are visited before predecessors, - // which is required for deterministic Boolean or BDD analysis. - visited.clear(); + // 4. Compute Topological Order + DenseSet visitedTopo; SmallVector order; std::function topo = [&](Block *u) { - if (!u || visited.contains(u)) - return; - visited.insert(u); + if (!u || visitedTopo.contains(u)) return; + visitedTopo.insert(u); if (auto *term = u->getTerminator()) - for (auto it = term->successor_begin(), e = term->successor_end(); - it != e; ++it) + for (auto it = term->successor_begin(), e = term->successor_end(); it != e; ++it) topo(*it); order.push_back(u); }; - topo(entry); + topo(L->newProd); std::reverse(order.begin(), order.end()); L->topoOrder = std::move(order); + // 5. Physical Reordering + // Reorder blocks in the region list to match the topological order. + // This does not change the graph structure (pointers), only the memory layout/print order. + for (Block *b : L->topoOrder) { + if (b != L->sinkBB) { + b->moveBefore(L->sinkBB); + } + } + return L; } @@ -862,7 +869,7 @@ static void insertDirectSuppression( Value muxCondition = nullptr; std::string funcName = funcOp.getName().str(); - std::string dir = "/home/yuaqin/dynamatic-scripts/TempOutputs/"; + std::string dir = "/home/yuaqin/new/dynamatic-scripts/TempOutputs/"; std::string cfgFile = dir + funcName + "_localcfg.txt"; std::string logFile = dir + funcName + "_debuglog.txt"; std::error_code EC_log; @@ -877,19 +884,13 @@ static void insertDirectSuppression( (consumer->getOperand(1) == connection || consumer->getOperand(2) == connection); - bool accountMuCondition = llvm::isa(consumer) && - consumer->hasAttr(FTD_EXPLICIT_MU) && - (consumer->getOperand(1) == connection || - consumer->getOperand(2) == connection); - if (accountMuCondition) - out << "[FTD] Accounting for MU condition\n"; // Get the control dependencies from the producer DenseSet prodControlDeps = cdAnalysis[producerBlock].forwardControlDeps; // Get the control dependencies from the consumer DenseSet consControlDeps = - cdAnalysis[consumer->getBlock()].forwardControlDeps; + cdAnalysis[consumerBlock].forwardControlDeps; out << "[FTD] Producer block: "; if (producerBlock) @@ -950,30 +951,6 @@ static void insertDirectSuppression( if (fProd->type == experimental::boolean::ExpressionType::Zero) return; - Block *anotherMuInputBlock = nullptr; - if (accountMuCondition) { - // Pick the other data input of the MU mux - Value anotherInput = (consumer->getOperand(1) == connection) - ? consumer->getOperand(2) - : consumer->getOperand(1); - - Operation *owner = anotherInput.getDefiningOp(); - - // Traverse upwards while the current owner is a conditional branch - while (auto cbr = llvm::dyn_cast_or_null(owner)) { - Value dataInput = cbr->getOperand(1); - owner = dataInput.getDefiningOp(); - } - - if (owner) - anotherMuInputBlock = owner->getBlock(); - else - llvm::report_fatal_error("MU input value has no defining operation."); - } - - ArrayRef anotherMuInputBlocks = - anotherMuInputBlock ? ArrayRef(&anotherMuInputBlock, 1) - : ArrayRef({}); auto locGraph = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock, bi); @@ -981,10 +958,6 @@ static void insertDirectSuppression( DenseSet locConsControlDepsTmp = locCDA.getAllBlockDeps()[locGraph->newCons].allControlDeps; - out << "[FTD] Avoid Block: "; - if (anotherMuInputBlock) - anotherMuInputBlock->printAsOperand(out); - out << "\n"; DenseSet locConsControlDeps; for (Block *nb : locConsControlDepsTmp) { Block *orig = locGraph->origMap.lookup(nb); @@ -993,8 +966,7 @@ static void insertDirectSuppression( } BoolExpression *fCons = - enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps, - anotherMuInputBlocks); + enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps); // Debug: append all LocalCFGs to one txt file (no console output) { diff --git a/experimental/lib/Support/FtdSupport.cpp b/experimental/lib/Support/FtdSupport.cpp index ce800f0f4a..58c09fb7f4 100644 --- a/experimental/lib/Support/FtdSupport.cpp +++ b/experimental/lib/Support/FtdSupport.cpp @@ -134,7 +134,7 @@ static void dfsAllPaths(Block *start, Block *end, std::vector &path, // Do not run DFS if the successor is in the list of blocks to traverse bool incorrectPath = false; for (auto *toAvoid : blocksToAvoid) { - if (toAvoid == successor) { + if (toAvoid == successor && bi.isGreater(toAvoid, blockToTraverse)) { incorrectPath = true; break; } From fe44fc012ac5c44a8c48179bb89cb78b1e0959d7 Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sun, 7 Dec 2025 01:38:55 +0100 Subject: [PATCH 12/15] a correct version --- .../lib/Support/FtdImplementation.cpp | 204 +++++++++++------- 1 file changed, 130 insertions(+), 74 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index ad9e651d43..19b15cb9d4 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -160,36 +160,144 @@ static void eliminateCommonBlocks(DenseSet &s1, } } +/// A lightweight DFS to check if 'end' is reachable from 'start'. +static bool isReachable(Block *start, Block *end) { + if (start == end) return true; + + DenseSet visited; + SmallVector stack; + stack.push_back(start); + visited.insert(start); + + while (!stack.empty()) { + Block *curr = stack.pop_back_val(); + + if (curr == end) return true; + + for (Block *succ : curr->getSuccessors()) { + if (!visited.count(succ)) { + visited.insert(succ); + stack.push_back(succ); + } + } + } + return false; +} + +struct LocalCFG { + // The MLIR region representing the local subgraph. + Region *region = nullptr; + // Mapping: block in local graph -> original block. + DenseMap origMap; + // The producer block in the local CFG. + Block *newProd = nullptr; + // The consumer block in the local CFG. + Block *newCons = nullptr; + // A replicated block used for self-loop delivery (Producer == Consumer). + Block *secondVisitBB = nullptr; + // A unique sink (exit) block to which all terminal paths lead. + Block *sinkBB = nullptr; + // Topological order of the reconstructed region. + SmallVector topoOrder; + // Temporary parent operation that owns the region. + Operation *containerOp = nullptr; + + ~LocalCFG() = default; +}; + /// The boolean condition to either generate or suppress a token are computed /// by considering all the paths from the producer (`start`) to the consumer /// (`end`). "Each path identifies a Boolean product of elementary conditions /// expressing the reaching of the target BB from the corresponding member of /// the set; the product of all such paths are added". -static BoolExpression *enumeratePaths(Block *start, Block *end, +static BoolExpression *enumeratePaths(const LocalCFG &lcfg, const ftd::BlockIndexing &bi, const DenseSet &controlDeps) { - // Start with a boolean expression of zero (so that new conditions can be - // added) - BoolExpression *sop = BoolExpression::boolZero(); + + // 1. Path Finding using Iterative DFS + std::vector> allPaths; + + struct StackFrame { + Block *u; + unsigned currIdx; + unsigned numSuccs; + }; - // Find all the paths from the producer to the consumer, using a DFS - std::vector> allPaths = findAllPaths(start, end, bi); + std::vector dfsStack; + std::vector currentLocalPath; // Acts as 'visited in current path' - // If the start and end block are the same (e.g., BB0 to BB0) and there is no - // real path between them, then consider the sop = 1 - if (start == end && allPaths.size() == 0) - sop = BoolExpression::boolOne(); + if (lcfg.newProd && lcfg.newCons) { + Block *root = lcfg.newProd; + auto *term = root->getTerminator(); + unsigned n = term ? term->getNumSuccessors() : 0; + + dfsStack.push_back({root, 0, n}); + currentLocalPath.push_back(root); + } else { + return BoolExpression::boolZero(); + } - // For each path - for (const std::vector &path : allPaths) { + while (!dfsStack.empty()) { + StackFrame &frame = dfsStack.back(); + + // --- Case A: Reached Consumer --- + if (frame.u == lcfg.newCons) { + std::vector origPath; + bool validMapping = true; + + for (Block *lb : currentLocalPath) { + Block *ob = lcfg.origMap.lookup(lb); + if (!ob && lb == lcfg.secondVisitBB) { + ob = lcfg.origMap.lookup(lcfg.newProd); + } + if (!ob) { validMapping = false; break; } + origPath.push_back(ob); + } + if (validMapping) allPaths.push_back(origPath); + + currentLocalPath.pop_back(); + dfsStack.pop_back(); + continue; + } + + // --- Case B: Traverse Successors --- + if (frame.currIdx < frame.numSuccs) { + auto *term = frame.u->getTerminator(); + Block *succ = term->getSuccessor(frame.currIdx); + frame.currIdx++; + + // 1. Skip Sink (Suppression) + // 2. [CRITICAL FIX] Skip Cycle: Check if succ is already in current path + // This prevents infinite loops while allowing the structure to exist. + bool isCycle = std::find(currentLocalPath.begin(), currentLocalPath.end(), succ) != currentLocalPath.end(); + + if (succ != lcfg.sinkBB && !isCycle) { + auto *succTerm = succ->getTerminator(); + unsigned succN = succTerm ? succTerm->getNumSuccessors() : 0; + + dfsStack.push_back({succ, 0, succN}); + currentLocalPath.push_back(succ); + } + } + // --- Case C: Backtrack --- + else { + currentLocalPath.pop_back(); + dfsStack.pop_back(); + } + } + + if (allPaths.empty()) + return BoolExpression::boolZero(); + + // 2. Expression Generation + BoolExpression *sop = BoolExpression::boolZero(); + + for (const std::vector &path : allPaths) { DenseSet tempCofactorSet; - // Compute the product of the conditions which allow that path to be - // executed BoolExpression *minterm = getPathExpression(path, tempCofactorSet, bi, controlDeps, false); - // Add the value to the result sop = BoolExpression::boolOr(sop, minterm); } return sop->boolMinimizeSop(); @@ -655,27 +763,6 @@ static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, return muxOp.getResult(); } -struct LocalCFG { - // The MLIR region representing the local subgraph. - Region *region = nullptr; - // Mapping: block in local graph -> original block. - DenseMap origMap; - // The producer block in the local CFG. - Block *newProd = nullptr; - // The consumer block in the local CFG. - Block *newCons = nullptr; - // A replicated block used for self-loop delivery (Producer == Consumer). - Block *secondVisitBB = nullptr; - // A unique sink (exit) block to which all terminal paths lead. - Block *sinkBB = nullptr; - // Topological order of the reconstructed region. - SmallVector topoOrder; - // Temporary parent operation that owns the region. - Operation *containerOp = nullptr; - - ~LocalCFG() = default; -}; - /// Build a local control-flow subgraph (LocalCFG) between a producer and /// consumer. The subgraph is reconstructed as a region with unique entry /// (producer) and exit (sink). @@ -945,18 +1032,17 @@ static void insertDirectSuppression( // Get rid of common entries in the two sets eliminateCommonBlocks(prodControlDeps, consControlDeps); - // Compute the activation function of producer and consumer - BoolExpression *fProd = - enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); - if (fProd->type == experimental::boolean::ExpressionType::Zero) - return; + // If producer is unreachable, the suppression is not needed. + if (!isReachable(entryBlock, producerBlock)) { + return; + } auto locGraph = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock, bi); ControlDependenceAnalysis locCDA(*locGraph->region); DenseSet locConsControlDepsTmp = - locCDA.getAllBlockDeps()[locGraph->newCons].allControlDeps; + locCDA.getAllBlockDeps()[locGraph->newCons].forwardControlDeps; DenseSet locConsControlDeps; for (Block *nb : locConsControlDepsTmp) { @@ -965,8 +1051,8 @@ static void insertDirectSuppression( locConsControlDeps.insert(orig); } - BoolExpression *fCons = - enumeratePaths(producerBlock, consumerBlock, bi, locConsControlDeps); + BoolExpression *fCons = + enumeratePaths(*locGraph, bi, locConsControlDeps); // Debug: append all LocalCFGs to one txt file (no console output) { @@ -1120,40 +1206,10 @@ static void insertDirectSuppression( } // f_supp = f_prod and not f_cons - out << "fProd = " << fProd->toString() << "\n"; out << "fCons = " << fCons->toString() << "\n"; BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); out << "fSupmin = " << fSup->toString() << "\n"; - if (fProd->type == experimental::boolean::ExpressionType::Zero) { - out << "[FTD] fProd == 0 detected\n"; - out << " producer block: "; - if (producerBlock) - producerBlock->printAsOperand(out); - else - out << ""; - out << "\n"; - - out << " consumer block: "; - if (consumerBlock) - consumerBlock->printAsOperand(out); - else - out << ""; - out << "\n"; - - out << " producer (connection): "; - if (Operation *def = connection.getDefiningOp()) { - out << def->getName() << " @ "; - def->getLoc().print(out); - } else { - out << ""; - } - out << "\n"; - - out << " consumer: " << consumer->getName() << " @ "; - consumer->getLoc().print(out); - out << "\n"; - } // If the activation function is not zero, then a suppress block is to be // inserted From 93c9927941e895ba9f8e377986630f97b94f364b Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sun, 7 Dec 2025 20:49:45 +0100 Subject: [PATCH 13/15] debuglog option --- .../lib/Support/FtdImplementation.cpp | 140 ++++++++++-------- 1 file changed, 77 insertions(+), 63 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index 19b15cb9d4..eb62346e73 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -954,14 +954,15 @@ static void insertDirectSuppression( Block *producerBlock = connection.getParentBlock(); Block *consumerBlock = consumer->getBlock(); Value muxCondition = nullptr; - + + bool debuglog = false; std::string funcName = funcOp.getName().str(); std::string dir = "/home/yuaqin/new/dynamatic-scripts/TempOutputs/"; std::string cfgFile = dir + funcName + "_localcfg.txt"; std::string logFile = dir + funcName + "_debuglog.txt"; std::error_code EC_log; llvm::raw_fd_ostream log(logFile, EC_log, - static_cast(0x0004)); + static_cast(0x0004)); llvm::raw_ostream &out = EC_log ? llvm::errs() : log; // Account for the condition of a Mux only if it corresponds to a GAMMA GSA @@ -979,43 +980,45 @@ static void insertDirectSuppression( DenseSet consControlDeps = cdAnalysis[consumerBlock].forwardControlDeps; - out << "[FTD] Producer block: "; - if (producerBlock) - producerBlock->printAsOperand(out); - else - out << "(null)"; - out << ", Consumer block: "; - if (consumerBlock) - consumerBlock->printAsOperand(out); - else - out << "(null)"; - out << "\n"; - // Debug: dump consumer block control deps - { - Block *consumerBlock = consumer->getBlock(); - auto &prodEntry = cdAnalysis[producerBlock]; - auto &depsEntry = cdAnalysis[consumerBlock]; + if (debuglog) { + out << "[FTD] Producer block: "; + if (producerBlock) + producerBlock->printAsOperand(out); + else + out << "(null)"; + out << ", Consumer block: "; + if (consumerBlock) + consumerBlock->printAsOperand(out); + else + out << "(null)"; + out << "\n"; + // Debug: dump consumer block control deps + { + Block *consumerBlock = consumer->getBlock(); + auto &prodEntry = cdAnalysis[producerBlock]; + auto &depsEntry = cdAnalysis[consumerBlock]; - auto printBlockSet = [&](llvm::StringRef label, - const DenseSet &S) { - out << label << " = { "; - bool first = true; - for (Block *b : S) { - if (!first) - out << ", "; - if (b) - b->printAsOperand(out); - else - out << ""; - first = false; - } - out << " }\n"; - }; - printBlockSet("[FTD] prod forwardControlDeps", - prodEntry.forwardControlDeps); - printBlockSet("[FTD] cons forwardControlDeps", - depsEntry.forwardControlDeps); - printBlockSet("[FTD] cons allControlDeps", depsEntry.allControlDeps); + auto printBlockSet = [&](llvm::StringRef label, + const DenseSet &S) { + out << label << " = { "; + bool first = true; + for (Block *b : S) { + if (!first) + out << ", "; + if (b) + b->printAsOperand(out); + else + out << ""; + first = false; + } + out << " }\n"; + }; + printBlockSet("[FTD] prod forwardControlDeps", + prodEntry.forwardControlDeps); + printBlockSet("[FTD] cons forwardControlDeps", + depsEntry.forwardControlDeps); + printBlockSet("[FTD] cons allControlDeps", depsEntry.allControlDeps); + } } // If the mux condition is to be taken into account, then the control @@ -1037,7 +1040,6 @@ static void insertDirectSuppression( return; } - auto locGraph = buildLocalCFGRegion(rewriter, producerBlock, consumerBlock, bi); ControlDependenceAnalysis locCDA(*locGraph->region); @@ -1054,8 +1056,7 @@ static void insertDirectSuppression( BoolExpression *fCons = enumeratePaths(*locGraph, bi, locConsControlDeps); - // Debug: append all LocalCFGs to one txt file (no console output) - { + if (debuglog) { std::error_code EC; llvm::raw_fd_ostream file(cfgFile, EC, static_cast(0x0004)); @@ -1134,7 +1135,7 @@ static void insertDirectSuppression( } } - { + if (debuglog) { auto printBlockSet = [&](llvm::StringRef label, const DenseSet &S) { out << label << " = { "; @@ -1160,7 +1161,7 @@ static void insertDirectSuppression( Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); DenseSet condControlDeps = cdAnalysis[muxConditionBlock].forwardControlDeps; - { + if (debuglog) { auto printBlockSet = [&](llvm::StringRef label, const DenseSet &S) { out << label << " = { "; @@ -1180,36 +1181,49 @@ static void insertDirectSuppression( printBlockSet("[FTD] muxControlDeps", condControlDeps); } } - out << "fCons-no-mux = " << fCons->toString() << "\n"; + if (debuglog) { + out << "fCons-no-mux = " << fCons->toString() << "\n"; + } + if (accountMuxCondition) { Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); BoolExpression *selectOperandCondition = BoolExpression::parseSop(bi.getBlockCondition(muxConditionBlock)); - - out << "[MUX] Mux Condition Block: "; - if (muxConditionBlock) - muxConditionBlock->printAsOperand(out); - else - out << "(null)"; - out << "\n"; + if (debuglog) { + out << "[MUX] Mux Condition Block: "; + if (muxConditionBlock) + muxConditionBlock->printAsOperand(out); + else + out << "(null)"; + out << "\n"; + } if (!bi.isLess(muxConditionBlock, producerBlock)) { if (consumer->getOperand(1) == connection) { - out << "MuxCondN = " - << (selectOperandCondition->boolNegate())->toString() << "\n"; - fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); + if (debuglog) { + out << "MuxCondN = " + << (selectOperandCondition->boolNegate())->toString() << "\n"; + selectOperandCondition->boolNegate(); + } + fCons = BoolExpression::boolAnd(fCons, selectOperandCondition->boolNegate()); } else { - out << "MuxCond = " << selectOperandCondition->toString() << "\n"; + if (debuglog) { + out << "MuxCond = " << selectOperandCondition->toString() << "\n"; + } fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); } } } + if (debuglog) { + out << "fCons = " << fCons->toString() << "\n"; + } // f_supp = f_prod and not f_cons - out << "fCons = " << fCons->toString() << "\n"; BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); - out << "fSupmin = " << fSup->toString() << "\n"; + if (debuglog){ + out << "fSupmin = " << fSup->toString() << "\n"; + } // If the activation function is not zero, then a suppress block is to be // inserted @@ -1232,12 +1246,12 @@ static void insertDirectSuppression( llvm::sort(tmp, [](auto &a, auto &b) { return a.first < b.first; }); for (auto &p : tmp) cofactorList.push_back(p.second); - - llvm::errs() << "[CofactorList] "; - for (const auto &s : cofactorList) - llvm::errs() << s << " "; - llvm::errs() << "\n"; - + if (debuglog) { + llvm::errs() << "[CofactorList] "; + for (const auto &s : cofactorList) + llvm::errs() << s << " "; + llvm::errs() << "\n"; + } BDD *bdd = buildBDD(fSup, cofactorList); Value branchCond = bddToCircuit(rewriter, bdd, consumer->getBlock(), bi); From 9fa5f04ec9b99b08801d4803b8bc255314a8413c Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Fri, 9 Jan 2026 16:53:49 +0100 Subject: [PATCH 14/15] update --- .../lib/Support/FtdImplementation.cpp | 207 ++++++++++-------- 1 file changed, 115 insertions(+), 92 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index b00da4a036..8c5e27e33e 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -303,6 +303,29 @@ static BoolExpression *enumeratePaths(const LocalCFG &lcfg, return sop->boolMinimizeSop(); } +/// Get a boolean expression representing the exit condition of the current +/// loop block. +static BoolExpression *getBlockLoopExitCondition(Block *loopExit, CFGLoop *loop, + CFGLoopInfo &li, + const ftd::BlockIndexing &bi) { + + // Get the boolean expression associated to the block exit + BoolExpression *blockCond = + BoolExpression::parseSop(bi.getBlockCondition(loopExit)); + + // Since we are in a loop, the terminator is a conditional branch. + auto *terminatorOperation = loopExit->getTerminator(); + auto condBranch = dyn_cast(terminatorOperation); + assert(condBranch && "Terminator of a loop must be `cf::CondBranchOp`"); + + // If the destination of the false outcome is not the block, then the + // condition must be negated + if (li.getLoopFor(condBranch.getFalseDest()) != loop) + blockCond->boolNegate(); + + return blockCond; +} + /// Run the Cytron algorithm to determine, give a set of values, in which blocks /// should we add a merge in order for those values to be merged static DenseSet @@ -596,6 +619,96 @@ getLoopExitCondition(CFGLoop *loop, std::vector *cofactorList, return fLoopExit; } +/// Starting from a boolean expression which is a single variable (either +/// direct or complement) return its corresponding circuit equivalent. This +/// means, either we obtain the output of the operation determining the +/// condition, or we add a `not` to complement. +static Value boolVariableToCircuit(PatternRewriter &rewriter, + experimental::boolean::BoolExpression *expr, + Block *block, const ftd::BlockIndexing &bi) { + + // Convert the expression into a single condition (for instance, `c0` or + // `~c0`). + SingleCond *singleCond = static_cast(expr); + + // Use the BlockIndexing to access the block corresponding to such condition + // and access its terminator to determine the condition. + auto conditionOpt = bi.getBlockFromCondition(singleCond->id); + if (!conditionOpt.has_value()) { + llvm::errs() << "Cannot obtain block condition from `BlockIndexing`\n"; + return nullptr; + } + auto condition = conditionOpt.value()->getTerminator()->getOperand(0); + + // Add a not if the condition is negated. + if (singleCond->isNegated) { + rewriter.setInsertionPointToStart(block); + auto notOp = rewriter.create( + block->getOperations().front().getLoc(), + ftd::channelifyType(condition.getType()), condition); + notOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); + return notOp->getResult(0); + } + condition.setType(ftd::channelifyType(condition.getType())); + return condition; +} + +/// Get a circuit out a boolean expression, depending on the different kinds +/// of expressions you might have. +static Value boolExpressionToCircuit(PatternRewriter &rewriter, + BoolExpression *expr, Block *block, + const ftd::BlockIndexing &bi) { + + // Variable case + if (expr->type == ExpressionType::Variable) + return boolVariableToCircuit(rewriter, expr, block, bi); + + // Constant case (either 0 or 1) + rewriter.setInsertionPointToStart(block); + auto sourceOp = rewriter.create( + block->getOperations().front().getLoc()); + Value cnstTrigger = sourceOp.getResult(); + + auto intType = rewriter.getIntegerType(1); + auto cstAttr = rewriter.getIntegerAttr( + intType, (expr->type == ExpressionType::One ? 1 : 0)); + + auto constOp = rewriter.create( + block->getOperations().front().getLoc(), cstAttr, cnstTrigger); + + constOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); + + return constOp.getResult(); +} + +/// Convert a `BDD` object as obtained from the bdd expansion to a +/// circuit +static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, + const ftd::BlockIndexing &bi) { + if (!bdd->successors.has_value()) + return boolExpressionToCircuit(rewriter, bdd->boolVariable, block, bi); + + rewriter.setInsertionPointToStart(block); + + // Get the two operands by recursively calling `bddToCircuit` (it possibly + // creates other muxes in a hierarchical way) + SmallVector muxOperands; + muxOperands.push_back( + bddToCircuit(rewriter, bdd->successors.value().first, block, bi)); + muxOperands.push_back( + bddToCircuit(rewriter, bdd->successors.value().second, block, bi)); + Value muxCond = + boolExpressionToCircuit(rewriter, bdd->boolVariable, block, bi); + + // Create the multiplxer and add it to the rest of the circuit + auto muxOp = rewriter.create( + block->getOperations().front().getLoc(), muxOperands[0].getType(), + muxCond, muxOperands); + muxOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); + + return muxOp.getResult(); +} + void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, handshake::FuncOp &funcOp, Operation *consumerOp, Value operand) { @@ -661,7 +774,7 @@ void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, if (size(cofactorList) > 1) { BDD *bdd = buildBDD(exitCondition, cofactorList); conditionValue = - bddToCircuit(rewriter, bdd, loop->getHeader(), bi, false); + bddToCircuit(rewriter, bdd, loop->getHeader(), bi); } else conditionValue = loop->getExitingBlock()->getTerminator()->getOperand(0); @@ -709,96 +822,6 @@ void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, consumerOp->replaceUsesOfWith(operand, regeneratedValue); } -/// Starting from a boolean expression which is a single variable (either -/// direct or complement) return its corresponding circuit equivalent. This -/// means, either we obtain the output of the operation determining the -/// condition, or we add a `not` to complement. -static Value boolVariableToCircuit(PatternRewriter &rewriter, - experimental::boolean::BoolExpression *expr, - Block *block, const ftd::BlockIndexing &bi) { - - // Convert the expression into a single condition (for instance, `c0` or - // `~c0`). - SingleCond *singleCond = static_cast(expr); - - // Use the BlockIndexing to access the block corresponding to such condition - // and access its terminator to determine the condition. - auto conditionOpt = bi.getBlockFromCondition(singleCond->id); - if (!conditionOpt.has_value()) { - llvm::errs() << "Cannot obtain block condition from `BlockIndexing`\n"; - return nullptr; - } - auto condition = conditionOpt.value()->getTerminator()->getOperand(0); - - // Add a not if the condition is negated. - if (singleCond->isNegated) { - rewriter.setInsertionPointToStart(block); - auto notOp = rewriter.create( - block->getOperations().front().getLoc(), - ftd::channelifyType(condition.getType()), condition); - notOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); - return notOp->getResult(0); - } - condition.setType(ftd::channelifyType(condition.getType())); - return condition; -} - -/// Get a circuit out a boolean expression, depending on the different kinds -/// of expressions you might have. -static Value boolExpressionToCircuit(PatternRewriter &rewriter, - BoolExpression *expr, Block *block, - const ftd::BlockIndexing &bi) { - - // Variable case - if (expr->type == ExpressionType::Variable) - return boolVariableToCircuit(rewriter, expr, block, bi); - - // Constant case (either 0 or 1) - rewriter.setInsertionPointToStart(block); - auto sourceOp = rewriter.create( - block->getOperations().front().getLoc()); - Value cnstTrigger = sourceOp.getResult(); - - auto intType = rewriter.getIntegerType(1); - auto cstAttr = rewriter.getIntegerAttr( - intType, (expr->type == ExpressionType::One ? 1 : 0)); - - auto constOp = rewriter.create( - block->getOperations().front().getLoc(), cstAttr, cnstTrigger); - - constOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); - - return constOp.getResult(); -} - -/// Convert a `BDD` object as obtained from the bdd expansion to a -/// circuit -static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, - const ftd::BlockIndexing &bi) { - if (!bdd->successors.has_value()) - return boolExpressionToCircuit(rewriter, bdd->boolVariable, block, bi); - - rewriter.setInsertionPointToStart(block); - - // Get the two operands by recursively calling `bddToCircuit` (it possibly - // creates other muxes in a hierarchical way) - SmallVector muxOperands; - muxOperands.push_back( - bddToCircuit(rewriter, bdd->successors.value().first, block, bi)); - muxOperands.push_back( - bddToCircuit(rewriter, bdd->successors.value().second, block, bi)); - Value muxCond = - boolExpressionToCircuit(rewriter, bdd->boolVariable, block, bi); - - // Create the multiplxer and add it to the rest of the circuit - auto muxOp = rewriter.create( - block->getOperations().front().getLoc(), muxOperands[0].getType(), - muxCond, muxOperands); - muxOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); - - return muxOp.getResult(); -} - /// Build a local control-flow subgraph (LocalCFG) between a producer and /// consumer. The subgraph is reconstructed as a region with unique entry /// (producer) and exit (sink). @@ -1507,7 +1530,7 @@ LogicalResult experimental::ftd::addGsaGates(Region ®ion, BDD *bdd = buildBDD(gate->condition, gate->cofactorList); // Convert the boolean expression obtained through BDD to a circuit conditionValue = - bddToCircuit(rewriter, bdd, gate->getBlock(), bi, false); + bddToCircuit(rewriter, bdd, gate->getBlock(), bi); } else conditionValue = gate->conditionBlock->getTerminator()->getOperand(0); From 57ef872ba0cb26ad5a9675adcd03847d5097340b Mon Sep 17 00:00:00 2001 From: QinYuan2000 Date: Sat, 10 Jan 2026 16:59:01 +0100 Subject: [PATCH 15/15] format --- .../lib/Support/FtdImplementation.cpp | 152 ++++++++++-------- 1 file changed, 82 insertions(+), 70 deletions(-) diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index 8c5e27e33e..10ec06cfdc 100644 --- a/experimental/lib/Support/FtdImplementation.cpp +++ b/experimental/lib/Support/FtdImplementation.cpp @@ -162,7 +162,8 @@ static void eliminateCommonBlocks(DenseSet &s1, /// A lightweight DFS to check if 'end' is reachable from 'start'. static bool isReachable(Block *start, Block *end) { - if (start == end) return true; + if (start == end) + return true; DenseSet visited; SmallVector stack; @@ -171,8 +172,9 @@ static bool isReachable(Block *start, Block *end) { while (!stack.empty()) { Block *curr = stack.pop_back_val(); - - if (curr == end) return true; + + if (curr == end) + return true; for (Block *succ : curr->getSuccessors()) { if (!visited.count(succ)) { @@ -213,10 +215,10 @@ struct LocalCFG { static BoolExpression *enumeratePaths(const LocalCFG &lcfg, const ftd::BlockIndexing &bi, const DenseSet &controlDeps) { - + // 1. Path Finding using Iterative DFS std::vector> allPaths; - + struct StackFrame { Block *u; unsigned currIdx; @@ -230,7 +232,7 @@ static BoolExpression *enumeratePaths(const LocalCFG &lcfg, Block *root = lcfg.newProd; auto *term = root->getTerminator(); unsigned n = term ? term->getNumSuccessors() : 0; - + dfsStack.push_back({root, 0, n}); currentLocalPath.push_back(root); } else { @@ -244,17 +246,21 @@ static BoolExpression *enumeratePaths(const LocalCFG &lcfg, if (frame.u == lcfg.newCons) { std::vector origPath; bool validMapping = true; - + for (Block *lb : currentLocalPath) { Block *ob = lcfg.origMap.lookup(lb); if (!ob && lb == lcfg.secondVisitBB) { - ob = lcfg.origMap.lookup(lcfg.newProd); + ob = lcfg.origMap.lookup(lcfg.newProd); + } + if (!ob) { + validMapping = false; + break; } - if (!ob) { validMapping = false; break; } origPath.push_back(ob); } - if (validMapping) allPaths.push_back(origPath); + if (validMapping) + allPaths.push_back(origPath); currentLocalPath.pop_back(); dfsStack.pop_back(); @@ -270,16 +276,17 @@ static BoolExpression *enumeratePaths(const LocalCFG &lcfg, // 1. Skip Sink (Suppression) // 2. [CRITICAL FIX] Skip Cycle: Check if succ is already in current path // This prevents infinite loops while allowing the structure to exist. - bool isCycle = std::find(currentLocalPath.begin(), currentLocalPath.end(), succ) != currentLocalPath.end(); + bool isCycle = std::find(currentLocalPath.begin(), currentLocalPath.end(), + succ) != currentLocalPath.end(); if (succ != lcfg.sinkBB && !isCycle) { auto *succTerm = succ->getTerminator(); unsigned succN = succTerm ? succTerm->getNumSuccessors() : 0; - + dfsStack.push_back({succ, 0, succN}); currentLocalPath.push_back(succ); } - } + } // --- Case C: Backtrack --- else { currentLocalPath.pop_back(); @@ -773,8 +780,7 @@ void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, getLoopExitCondition(loop, &cofactorList, loopInfo, bi); if (size(cofactorList) > 1) { BDD *bdd = buildBDD(exitCondition, cofactorList); - conditionValue = - bddToCircuit(rewriter, bdd, loop->getHeader(), bi); + conditionValue = bddToCircuit(rewriter, bdd, loop->getHeader(), bi); } else conditionValue = loop->getExitingBlock()->getTerminator()->getOperand(0); @@ -856,11 +862,12 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, cloned[origProd] = entry; // DFS Function - std::function dfs = [&](Block *currOrig, Block *currNew) { + std::function dfs = [&](Block *currOrig, + Block *currNew) { visited.insert(currOrig); auto *term = currOrig->getTerminator(); - + // Dead End: Implicit flow to Sink. if (!term || term->getNumSuccessors() == 0) { builder.setInsertionPointToEnd(currNew); @@ -868,49 +875,50 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, return; } - // LIST 1: The distinct successors in the NEW Local CFG for the current block. - // Used to construct the BranchOp/CondBranchOp. + // LIST 1: The distinct successors in the NEW Local CFG for the current + // block. Used to construct the BranchOp/CondBranchOp. SmallVector localSuccessors; - // LIST 2: The successors that are valid and new, requiring further DFS traversal. - // Stored as pairs: {Original Successor, New Local Block}. - SmallVector, 2> successorsToVisit; + // LIST 2: The successors that are valid and new, requiring further DFS + // traversal. Stored as pairs: {Original Successor, New Local Block}. + SmallVector, 2> successorsToVisit; - for (auto it = term->successor_begin(), e = term->successor_end(); it != e; ++it) { + for (auto it = term->successor_begin(), e = term->successor_end(); it != e; + ++it) { Block *succOrig = *it; - Block *nextBlockInLocalCFG = nullptr; // Where the edge points to in the new graph + Block *nextBlockInLocalCFG = + nullptr; // Where the edge points to in the new graph // --- LOGIC: Determine the edge destination based on rules --- // Case 1: Consumer Reached (Valid Delivery) if (succOrig == origCons) { if (succOrig == origProd) { - // Self-loop delivery - if (!L->secondVisitBB) { - L->secondVisitBB = new Block(); - R.push_back(L->secondVisitBB); - L->origMap[L->secondVisitBB] = nullptr; - // Terminate SecondVisit immediately to Sink - OpBuilder::InsertionGuard g(builder); - builder.setInsertionPointToEnd(L->secondVisitBB); - builder.create(loc, L->sinkBB); - } - nextBlockInLocalCFG = L->secondVisitBB; - L->newCons = L->secondVisitBB; - } - else { - // Standard delivery - if (!L->newCons || L->newCons == L->secondVisitBB) { - Block *proxy = new Block(); - R.push_back(proxy); - L->origMap[proxy] = succOrig; - L->newCons = proxy; - // Terminate Proxy immediately to Sink - OpBuilder::InsertionGuard g(builder); - builder.setInsertionPointToEnd(proxy); - builder.create(loc, L->sinkBB); - } - nextBlockInLocalCFG = L->newCons; + // Self-loop delivery + if (!L->secondVisitBB) { + L->secondVisitBB = new Block(); + R.push_back(L->secondVisitBB); + L->origMap[L->secondVisitBB] = nullptr; + // Terminate SecondVisit immediately to Sink + OpBuilder::InsertionGuard g(builder); + builder.setInsertionPointToEnd(L->secondVisitBB); + builder.create(loc, L->sinkBB); + } + nextBlockInLocalCFG = L->secondVisitBB; + L->newCons = L->secondVisitBB; + } else { + // Standard delivery + if (!L->newCons || L->newCons == L->secondVisitBB) { + Block *proxy = new Block(); + R.push_back(proxy); + L->origMap[proxy] = succOrig; + L->newCons = proxy; + // Terminate Proxy immediately to Sink + OpBuilder::InsertionGuard g(builder); + builder.setInsertionPointToEnd(proxy); + builder.create(loc, L->sinkBB); + } + nextBlockInLocalCFG = L->newCons; } } // Case 2: Producer revisited, consumer not reached (Invalid) @@ -920,9 +928,9 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, // Case 3: Visited else if (visited.count(succOrig)) { if (cloned.count(succOrig)) { - nextBlockInLocalCFG = cloned[succOrig]; + nextBlockInLocalCFG = cloned[succOrig]; } else { - nextBlockInLocalCFG = L->sinkBB; + nextBlockInLocalCFG = L->sinkBB; } } // Case 4: Invalid Back-edge @@ -946,14 +954,15 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, } // --- CONSTRUCTION: Create the branch instruction --- - + builder.setInsertionPointToEnd(currNew); if (localSuccessors.size() == 1) { builder.create(loc, localSuccessors[0]); } else if (localSuccessors.size() == 2) { // Placeholder condition for 2-way branches Value cond = builder.create(loc, 1, 1); - builder.create(loc, cond, localSuccessors[0], localSuccessors[1]); + builder.create(loc, cond, localSuccessors[0], + localSuccessors[1]); } else { // Default fall-through for complex control flow builder.create(loc, L->sinkBB); @@ -961,7 +970,7 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, // --- TRAVERSAL: Continue DFS --- for (auto &pair : successorsToVisit) { - dfs(pair.first, pair.second); + dfs(pair.first, pair.second); } }; @@ -972,16 +981,19 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, builder.setInsertionPointToEnd(L->sinkBB); builder.create(loc); - if (!L->newCons) L->newCons = L->sinkBB; + if (!L->newCons) + L->newCons = L->sinkBB; // 4. Compute Topological Order DenseSet visitedTopo; SmallVector order; std::function topo = [&](Block *u) { - if (!u || visitedTopo.contains(u)) return; + if (!u || visitedTopo.contains(u)) + return; visitedTopo.insert(u); if (auto *term = u->getTerminator()) - for (auto it = term->successor_begin(), e = term->successor_end(); it != e; ++it) + for (auto it = term->successor_begin(), e = term->successor_end(); + it != e; ++it) topo(*it); order.push_back(u); }; @@ -992,10 +1004,11 @@ buildLocalCFGRegion(OpBuilder &builder, Block *origProd, Block *origCons, // 5. Physical Reordering // Reorder blocks in the region list to match the topological order. - // This does not change the graph structure (pointers), only the memory layout/print order. + // This does not change the graph structure (pointers), only the memory + // layout/print order. for (Block *b : L->topoOrder) { if (b != L->sinkBB) { - b->moveBefore(L->sinkBB); + b->moveBefore(L->sinkBB); } } @@ -1013,7 +1026,7 @@ static void insertDirectSuppression( Block *producerBlock = connection.getParentBlock(); Block *consumerBlock = consumer->getBlock(); Value muxCondition = nullptr; - + bool debuglog = false; std::string funcName = funcOp.getName().str(); std::string dir = "/home/yuaqin/new/dynamatic-scripts/TempOutputs/"; @@ -1021,7 +1034,7 @@ static void insertDirectSuppression( std::string logFile = dir + funcName + "_debuglog.txt"; std::error_code EC_log; llvm::raw_fd_ostream log(logFile, EC_log, - static_cast(0x0004)); + static_cast(0x0004)); llvm::raw_ostream &out = EC_log ? llvm::errs() : log; // Account for the condition of a Mux only if it corresponds to a GAMMA GSA @@ -1058,7 +1071,7 @@ static void insertDirectSuppression( auto &depsEntry = cdAnalysis[consumerBlock]; auto printBlockSet = [&](llvm::StringRef label, - const DenseSet &S) { + const DenseSet &S) { out << label << " = { "; bool first = true; for (Block *b : S) { @@ -1096,7 +1109,7 @@ static void insertDirectSuppression( eliminateCommonBlocks(prodControlDeps, consControlDeps); // If producer is unreachable, the suppression is not needed. if (!isReachable(entryBlock, producerBlock)) { - return; + return; } auto locGraph = @@ -1112,8 +1125,7 @@ static void insertDirectSuppression( locConsControlDeps.insert(orig); } - BoolExpression *fCons = - enumeratePaths(*locGraph, bi, locConsControlDeps); + BoolExpression *fCons = enumeratePaths(*locGraph, bi, locConsControlDeps); if (debuglog) { std::error_code EC; @@ -1264,7 +1276,8 @@ static void insertDirectSuppression( << (selectOperandCondition->boolNegate())->toString() << "\n"; selectOperandCondition->boolNegate(); } - fCons = BoolExpression::boolAnd(fCons, selectOperandCondition->boolNegate()); + fCons = BoolExpression::boolAnd(fCons, + selectOperandCondition->boolNegate()); } else { if (debuglog) { out << "MuxCond = " << selectOperandCondition->toString() << "\n"; @@ -1280,7 +1293,7 @@ static void insertDirectSuppression( // f_supp = f_prod and not f_cons BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); - if (debuglog){ + if (debuglog) { out << "fSupmin = " << fSup->toString() << "\n"; } @@ -1529,8 +1542,7 @@ LogicalResult experimental::ftd::addGsaGates(Region ®ion, // cofactors BDD *bdd = buildBDD(gate->condition, gate->cofactorList); // Convert the boolean expression obtained through BDD to a circuit - conditionValue = - bddToCircuit(rewriter, bdd, gate->getBlock(), bi); + conditionValue = bddToCircuit(rewriter, bdd, gate->getBlock(), bi); } else conditionValue = gate->conditionBlock->getTerminator()->getOperand(0);