diff --git a/experimental/lib/Support/FtdImplementation.cpp b/experimental/lib/Support/FtdImplementation.cpp index 1ac75bd076..10ec06cfdc 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,88 +160,151 @@ 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(); +/// 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; } -/// 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])); -} +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(); - // Find all the paths from the producer to the consumer, using a DFS - std::vector> allPaths = findAllPaths(start, end, bi); + // 1. Path Finding using Iterative DFS + std::vector> allPaths; - // 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(); + struct StackFrame { + Block *u; + unsigned currIdx; + unsigned numSuccs; + }; - // For each path - for (const std::vector &path : allPaths) { + std::vector dfsStack; + std::vector currentLocalPath; // Acts as 'visited in current path' + + 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(); + } + + 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(); @@ -557,14 +602,37 @@ LogicalResult ftd::createPhiNetworkDeps( return success(); } +static BoolExpression * +getLoopExitCondition(CFGLoop *loop, std::vector *cofactorList, + mlir::CFGLoopInfo &li, const ftd::BlockIndexing &bi) { + + SmallVector exitBlocks; + loop->getExitingBlocks(exitBlocks); + + 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)); + fLoopExit = fLoopExit->boolMinimize(); + } + + // Sort the cofactors alphabetically + std::sort(cofactorList->begin(), cofactorList->end()); + + 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, - bool needsChannelify = true) { + Block *block, const ftd::BlockIndexing &bi) { // Convert the expression into a single condition (for instance, `c0` or // `~c0`). @@ -573,9 +641,10 @@ static Value boolVariableToCircuit(PatternRewriter &rewriter, // 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()) + 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. @@ -587,9 +656,7 @@ static Value boolVariableToCircuit(PatternRewriter &rewriter, notOp->setAttr(FTD_OP_TO_SKIP, rewriter.getUnitAttr()); return notOp->getResult(0); } - if (needsChannelify) { - condition.setType(ftd::channelifyType(condition.getType())); - } + condition.setType(ftd::channelifyType(condition.getType())); return condition; } @@ -597,12 +664,11 @@ static Value boolVariableToCircuit(PatternRewriter &rewriter, /// of expressions you might have. static Value boolExpressionToCircuit(PatternRewriter &rewriter, BoolExpression *expr, Block *block, - const ftd::BlockIndexing &bi, - bool needsChannelify = true) { + const ftd::BlockIndexing &bi) { // Variable case if (expr->type == ExpressionType::Variable) - return boolVariableToCircuit(rewriter, expr, block, bi, needsChannelify); + return boolVariableToCircuit(rewriter, expr, block, bi); // Constant case (either 0 or 1) rewriter.setInsertionPointToStart(block); @@ -625,23 +691,21 @@ static Value boolExpressionToCircuit(PatternRewriter &rewriter, /// 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, - bool needsChannelify = true) { + const ftd::BlockIndexing &bi) { if (!bdd->successors.has_value()) - return boolExpressionToCircuit(rewriter, bdd->boolVariable, block, bi, - needsChannelify); + 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, needsChannelify)); - muxOperands.push_back(bddToCircuit(rewriter, bdd->successors.value().second, - block, bi, needsChannelify)); - Value muxCond = boolExpressionToCircuit(rewriter, bdd->boolVariable, block, - bi, needsChannelify); + 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( @@ -652,30 +716,6 @@ static Value bddToCircuit(PatternRewriter &rewriter, BDD *bdd, Block *block, return muxOp.getResult(); } -static BoolExpression * -getLoopExitCondition(CFGLoop *loop, std::vector *cofactorList, - mlir::CFGLoopInfo &li, const ftd::BlockIndexing &bi) { - - SmallVector exitBlocks; - loop->getExitingBlocks(exitBlocks); - - 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)); - fLoopExit = fLoopExit->boolMinimize(); - } - - // Sort the cofactors alphabetically - std::sort(cofactorList->begin(), cofactorList->end()); - - return fLoopExit; -} - void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, handshake::FuncOp &funcOp, Operation *consumerOp, Value operand) { @@ -740,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, false); + conditionValue = bddToCircuit(rewriter, bdd, loop->getHeader(), bi); } else conditionValue = loop->getExitingBlock()->getTerminator()->getOperand(0); @@ -789,117 +828,191 @@ void ftd::addRegenOperandConsumer(PatternRewriter &rewriter, consumerOp->replaceUsesOfWith(operand, regeneratedValue); } -// 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); -} +/// 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, + const ftd::BlockIndexing &bi) { + auto L = std::make_unique(); + Location loc = builder.getUnknownLoc(); + + // Setup Region Container + 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; + + // 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(); + R.push_back(entry); + L->newProd = entry; + L->origMap[entry] = origProd; + + DenseMap cloned; + DenseSet visited; + cloned[origProd] = entry; + + // DFS Function + 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); + builder.create(loc, L->sinkBB); + return; + } -using PairOperandConsumer = std::pair; - -// Find the closest loop exit between a producer inside the loop and a consumer -// outside the loop. -static Block *findClosestLoopExit(Operation *consumer, Value connection, - const ftd::BlockIndexing &bi, - SmallVector exitBlocks) { - // Find all the paths from the producer to the consumer using DFS - std::vector> allPaths = - findAllPaths(connection.getParentBlock(), consumer->getBlock(), bi); - - Block *closestExit = nullptr; - unsigned minDistance = std::numeric_limits::max(); - - // For every path from producer to consumer , check if it passes through any - // of the loop’s exit blocks. - for (const auto &path : allPaths) { - bool foundInThisPath = false; - for (unsigned i = 0; i < path.size() && !foundInThisPath; ++i) { - Block *pathBlock = path[i]; - for (Block *exitBlock : exitBlocks) { - if (pathBlock == exitBlock) { - // Update the closest exit if this one is nearer to the producer - if (i < minDistance) { - minDistance = i; - closestExit = exitBlock; + // 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); } - foundInThisPath = true; - break; + 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) + else if (succOrig == origProd) { + nextBlockInLocalCFG = L->sinkBB; + } + // 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}); + } + + // Add the determined destination to the list of local successors + localSuccessors.push_back(nextBlockInLocalCFG); } - } - assert(closestExit && - "No loop exit found in any path between producer and consumer."); - return closestExit; -} + // --- 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]); + } else { + // Default fall-through for complex control flow + builder.create(loc, L->sinkBB); + } -/// 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) { + // --- TRAVERSAL: Continue DFS --- + for (auto &pair : successorsToVisit) { + dfs(pair.first, pair.second); + } + }; - handshake::ConditionalBranchOp branchOp; + // Start DFS + dfs(origProd, L->newProd); - // Do not add the branch in case of a while loop with backward edge - if (btlt == BackwardRelationship && isWhileLoop(loop)) - return connection; + // Finalize Sink + builder.setInsertionPointToEnd(L->sinkBB); + builder.create(loc); - std::vector cofactorList; - SmallVector exitBlocks; - loop->getExitingBlocks(exitBlocks); - BoolExpression *fLoopExit = getLoopExitCondition(loop, &cofactorList, li, bi); - // Choose the closest loop exit to the producer and place the suppression - // there - Block *loopExit = findClosestLoopExit(consumer, connection, bi, exitBlocks); - - // 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); + 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; + visitedTopo.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(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); + } } - consumer->replaceUsesOfWith(connection, newConnection); - return newConnection; + return L; } /// Apply the algorithm from FPL'22 to handle a non-loop situation of @@ -914,6 +1027,16 @@ static void insertDirectSuppression( 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)); + 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) && @@ -927,7 +1050,48 @@ static void insertDirectSuppression( // Get the control dependencies from the consumer DenseSet consControlDeps = - cdAnalysis[consumer->getBlock()].forwardControlDeps; + cdAnalysis[consumerBlock].forwardControlDeps; + + 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); + } + } // 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 @@ -943,40 +1107,223 @@ static void insertDirectSuppression( // Get rid of common entries in the two sets eliminateCommonBlocks(prodControlDeps, consControlDeps); + // 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].forwardControlDeps; + + DenseSet locConsControlDeps; + for (Block *nb : locConsControlDepsTmp) { + Block *orig = locGraph->origMap.lookup(nb); + if (orig) + locConsControlDeps.insert(orig); + } + + BoolExpression *fCons = enumeratePaths(*locGraph, bi, locConsControlDeps); + + if (debuglog) { + 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 (locGraph->newProd) + locGraph->newProd->printAsOperand(file); + else + file << ""; + file << "\nnewCons: "; + 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 : locGraph->region->getBlocks()) { + file << " new="; + b.printAsOperand(file); + Block *orig = locGraph->origMap.lookup(&b); + file << " -> orig="; + if (orig) + orig->printAsOperand(file); + else + file << ""; + file << "\n"; + } + + file << "\nEdges:\n"; + for (Block &b : locGraph->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 : locGraph->topoOrder) { + if (b) + b->printAsOperand(file); + else + file << ""; + file << ' '; + } + file << "\n\n"; + } + } + + if (debuglog) { + 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"; + }; - // Compute the activation function of producer and consumer - BoolExpression *fProd = - enumeratePaths(entryBlock, producerBlock, bi, prodControlDeps); - BoolExpression *fCons = - enumeratePaths(entryBlock, consumerBlock, bi, consControlDeps); + printBlockSet("[FTD] locConsControlDepsTmp", locConsControlDepsTmp); + printBlockSet("[FTD] locConsControlDeps", locConsControlDeps); + } + + if (accountMuxCondition) { + muxCondition = consumer->getOperand(0); + Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); + DenseSet condControlDeps = + cdAnalysis[muxConditionBlock].forwardControlDeps; + if (debuglog) { + 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] muxControlDeps", condControlDeps); + } + } + if (debuglog) { + out << "fCons-no-mux = " << fCons->toString() << "\n"; + } if (accountMuxCondition) { Block *muxConditionBlock = returnMuxConditionBlock(muxCondition); BoolExpression *selectOperandCondition = BoolExpression::parseSop(bi.getBlockCondition(muxConditionBlock)); + if (debuglog) { + out << "[MUX] Mux Condition Block: "; + if (muxConditionBlock) + muxConditionBlock->printAsOperand(out); + else + 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 (!prodControlDeps.contains(muxConditionBlock)) { - if (consumer->getOperand(1) == connection) + if (!bi.isLess(muxConditionBlock, producerBlock)) { + if (consumer->getOperand(1) == connection) { + if (debuglog) { + out << "MuxCondN = " + << (selectOperandCondition->boolNegate())->toString() << "\n"; + selectOperandCondition->boolNegate(); + } fCons = BoolExpression::boolAnd(fCons, selectOperandCondition->boolNegate()); - else + } else { + if (debuglog) { + out << "MuxCond = " << selectOperandCondition->toString() << "\n"; + } fCons = BoolExpression::boolAnd(fCons, selectOperandCondition); + } } } - /// f_supp = f_prod and not f_cons - BoolExpression *fSup = BoolExpression::boolAnd(fProd, fCons->boolNegate()); + if (debuglog) { + out << "fCons = " << fCons->toString() << "\n"; + } + // f_supp = f_prod and not f_cons + BoolExpression *fSup = fCons->boolNegate(); fSup = fSup->boolMinimize(); + if (debuglog) { + out << "fSupmin = " << fSup->toString() << "\n"; + } // If the activation function is not zero, then a suppress block is to be // inserted 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 : locGraph->topoOrder) + if (auto *ob = locGraph->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); + 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); @@ -997,6 +1344,7 @@ static void insertDirectSuppression( use.set(branchOp.getFalseResult()); } } + rewriter.eraseOp(locGraph->containerOp); } void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, @@ -1069,71 +1417,10 @@ void ftd::addSuppOperandConsumer(PatternRewriter &rewriter, 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 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) { @@ -1255,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, false); + conditionValue = bddToCircuit(rewriter, bdd, gate->getBlock(), bi); } else conditionValue = gate->conditionBlock->getTerminator()->getOperand(0); diff --git a/lib/Analysis/ControlDependenceAnalysis.cpp b/lib/Analysis/ControlDependenceAnalysis.cpp index 492ab1345e..86b12e78bb 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 + } + } } } }