2424#include < mlir/Support/IndentedOstream.h>
2525#include < mlir/Support/LogicalResult.h>
2626
27+ #include < deque>
28+ #include < map>
2729#include < memory>
28- #include < set>
30+ #include < optional>
31+ #include < utility>
2932
3033#define DEBUG_TYPE " xten-minimize-live"
3134
@@ -83,7 +86,7 @@ struct OpInfo {
8386mlir::LogicalResult verifyStrAttr (mlir::Operation *op, llvm::StringRef attrKey,
8487 llvm::StringRef attrValue) {
8588 if (!op->hasAttr (attrKey) ||
86- !(op->getAttr (attrKey). cast <StringAttr>( ).str () == attrValue)) {
89+ !(cast<StringAttr>( op->getAttr (attrKey)).str () == attrValue)) {
8790 return failure ();
8891 }
8992 return success ();
@@ -223,7 +226,7 @@ size_t getSize(Value val) {
223226 if (elementType.isIntOrFloat ())
224227 return (elementType.getIntOrFloatBitWidth () * type.getNumElements ()) / 8 ;
225228
226- if (auto complexType = elementType. dyn_cast <ComplexType>()) {
229+ if (auto complexType = dyn_cast<ComplexType>(elementType )) {
227230 elementType = complexType.getElementType ();
228231 return (elementType.getIntOrFloatBitWidth () * type.getNumElements () * 2 ) /
229232 8 ;
@@ -270,22 +273,28 @@ class XTenMinimizeLiveTensorsPass
270273 XTenMinimizeLiveTensorsPass (const XTenMinimizeLiveTensorsPass &pass) =
271274 default ;
272275
273- // Recursively collect the OpInfo of all FM producers.
274- [[nodiscard]] LogicalResult
275- collectOperandInfo (OpInfo const &opInfo) { // NOLINT(misc-no-recursion)
276+ // Collect the OpInfo of all FM producers.
277+ LogicalResult collectOperandInfo (OpInfo const &opInfo) {
276278 // Visit all FM operands to collect their OpInfo.
277- for (auto operand : opInfo.operands ) {
279+
280+ std::deque<std::pair<Value, const OpInfo &>> worklist;
281+ for (Value v : opInfo.operands )
282+ worklist.emplace_back (v, opInfo);
283+
284+ while (!worklist.empty ()) {
285+ const auto [operand, currentOpInfo] = worklist.front ();
286+ worklist.pop_front ();
278287 Operation *defOp = operand.getDefiningOp ();
279288 if (defOp == nullptr ) {
280289 // Use currFn as stand-in for BlockArguments.
281- assert (operand. isa <BlockArgument>());
290+ assert (isa<BlockArgument>(operand ));
282291 defOp = currFn;
283292 }
284293
285294 // If OpInfo is already created, so we only need to note this consumer.
286- auto prevInfoIt = opToInfo.find (defOp);
295+ const auto prevInfoIt = opToInfo.find (defOp);
287296 if (prevInfoIt != opToInfo.end ()) {
288- prevInfoIt->second .consumers .push_back (opInfo .op );
297+ prevInfoIt->second .consumers .push_back (currentOpInfo .op );
289298 continue ;
290299 }
291300
@@ -306,16 +315,15 @@ class XTenMinimizeLiveTensorsPass
306315 .operands = *fmOperands,
307316 .results = fmResults,
308317 .sharesResultMemory = sharesResultMemory,
309- .consumers = {opInfo.op }};
310- auto [opFwdIt, succeeded] = opToInfo.emplace (defOp, std::move (info));
311- setOpSizes (opFwdIt->second );
312-
313- // Recursively collect details of the operands of this operand.
314- LogicalResult result = collectOperandInfo (opFwdIt->second );
315- if (result.failed ()) {
316- signalPassFailure ();
317- return LogicalResult::failure ();
318- }
318+ .consumers = {currentOpInfo.op },
319+ .orderedProducers = {}};
320+ setOpSizes (info);
321+ const auto [opFwdIt, succeeded] =
322+ opToInfo.emplace (defOp, std::move (info));
323+ assert (succeeded && " unexpected duplicate op in opToInfo" );
324+
325+ for (auto v : llvm::reverse (opFwdIt->second .operands ))
326+ worklist.emplace_front (v, opFwdIt->second );
319327 }
320328 return LogicalResult::success ();
321329 }
@@ -341,100 +349,128 @@ class XTenMinimizeLiveTensorsPass
341349 });
342350 }
343351
344- // / Recursively determine branch running sizes.
352+ // / Determine branch running sizes.
345353 // /
346354 // / \p opInfo points to the info for the op being analyzed.
347355 // / \p brInfo incoming branch info, to be updated by this op.
348356 // / \p completed contains the BranchRunning info for any fully
349357 // / analyzed operations.
350- void determineBranchRunning (OpInfo *opInfo, // NOLINT(misc-no-recursion)
351- BranchRunning &brInfo,
358+ void determineBranchRunning (OpInfo *opInfo, BranchRunning brInfo,
352359 std::map<Operation *, BranchRunning> &completed) {
353- // Analyze simple fallthrough operations.
354- while (opInfo->operands .size () < 2 && opInfo->consumers .size () < 2 ) {
355- // Avoid recomputing BranchRunning for operations we already visited.
356- auto opIt = completed.find (opInfo->op );
357- if (opIt != completed.end ())
358- return ;
359-
360- brInfo.maxRunning = std::max (brInfo.maxRunning , opInfo->sizes .results );
361- brInfo.lastResults = opInfo->sizes .results ;
362- opInfo->orderedProducers = {brInfo.lastOp };
363- brInfo.lastOp = opInfo->op ;
364- completed.insert ({opInfo->op , brInfo});
365-
366- if (opInfo->consumers .empty ())
367- return ; // Nothing more to compute.
368- opInfo = &opToInfo.at (opInfo->consumers .front ());
369- }
370360
371- // Avoid recomputing BranchRunning for operations we already visited.
372- auto opIt = completed.find (opInfo->op );
373- if (opIt != completed.end ())
374- return ;
361+ std::deque<std::pair<OpInfo *, BranchRunning>> worklist;
362+
363+ // This lambda is used so that we can use returns from inner loops.
364+ // An alternative would be the use of gotos
365+ const auto determineBranchRunningImpl =
366+ [this , &worklist](OpInfo *opInfo, BranchRunning &brInfo,
367+ std::map<Operation *, BranchRunning> &completed) {
368+ // Analyze simple fallthrough operations.
369+ while (opInfo->operands .size () < 2 && opInfo->consumers .size () < 2 ) {
370+ // Avoid recomputing BranchRunning for operations we already
371+ // visited.
372+ auto opIt = completed.find (opInfo->op );
373+ if (opIt != completed.end ())
374+ return ;
375+
376+ brInfo.maxRunning =
377+ std::max (brInfo.maxRunning , opInfo->sizes .results );
378+ brInfo.lastResults = opInfo->sizes .results ;
379+ opInfo->orderedProducers = {brInfo.lastOp };
380+ brInfo.lastOp = opInfo->op ;
381+ completed.insert ({opInfo->op , brInfo});
382+
383+ if (opInfo->consumers .empty ())
384+ return ; // Nothing more to compute.
385+ opInfo = &opToInfo.at (opInfo->consumers .front ());
386+ }
375387
376- // At a joining point - collect this branch and proceed iff all incoming
377- // branches have been collected.
378- SmallVector<BranchRunning> branches;
379- for (Value val : opInfo->operands ) {
380- Operation *op = val.getDefiningOp ();
381- if (op == nullptr )
382- op = currFn; // BlockArgument stand-in.
383-
384- auto opIt = completed.find (op);
385- if (opIt == completed.end ())
386- return ; // Another producer needs to complete first.
387- branches.push_back (opIt->second );
388- }
388+ // Avoid recomputing BranchRunning for operations we already visited.
389+ auto opIt = completed.find (opInfo->op );
390+ if (opIt != completed.end ())
391+ return ;
392+
393+ // At a joining point - collect this branch and proceed iff all
394+ // incoming branches have been collected.
395+ SmallVector<BranchRunning> branches;
396+ for (Value val : opInfo->operands ) {
397+ Operation *op = val.getDefiningOp ();
398+ if (op == nullptr )
399+ op = currFn; // BlockArgument stand-in.
400+
401+ auto opIt = completed.find (op);
402+ if (opIt == completed.end ())
403+ return ; // Another producer needs to complete first.
404+ branches.push_back (opIt->second );
405+ }
389406
390- // Concat producers should be ordered according to their operands
391- // (op0 before op1 before op2, etc.), so no need to sort them out.
392- if (!isConcatSubgraph (opInfo->op )) {
393- std::sort (branches.begin (), branches.end (),
407+ // Concat producers should be ordered according to their operands
408+ // (op0 before op1 before op2, etc.), so no need to sort them out.
409+ if (!isConcatSubgraph (opInfo->op )) {
410+ std::sort (
411+ branches.begin (), branches.end (),
394412 [](BranchRunning &aBranch, BranchRunning &bBranch) -> bool {
395413 return (aBranch.maxRunning - aBranch.lastResults ) >
396414 (bBranch.maxRunning - bBranch.lastResults );
397415 });
398- } else if (hasAllGAPInputs (opInfo->op )) {
399- // For GAP concats we want right-to-left ordering of the inputs
400- std::reverse (branches.begin (), branches.end ());
401- }
416+ } else if (hasAllGAPInputs (opInfo->op )) {
417+ // For GAP concats we want right-to-left ordering of the inputs
418+ std::reverse (branches.begin (), branches.end ());
419+ }
402420
403- opInfo->orderedProducers .clear ();
404- for (BranchRunning const &branch : branches)
405- opInfo->orderedProducers .push_back (branch.lastOp );
406-
407- // Complete the brInfo for this operation.
408- size_t maxRunning = opInfo->sizes .running ;
409- for (BranchRunning const &branch : branches)
410- maxRunning = std::max (maxRunning, branch.maxRunning );
411- brInfo.maxRunning = maxRunning;
412- brInfo.lastResults = opInfo->sizes .results ;
413- brInfo.lastOp = opInfo->op ;
414- completed.insert ({opInfo->op , brInfo});
415-
416- // Continue to all consumers.
417- for (Operation *consumer : opInfo->consumers ) {
418- BranchRunning nextRunning{.maxRunning = maxRunning,
419- .lastResults = opInfo->sizes .results ,
420- .lastOp = opInfo->op };
421- determineBranchRunning (&opToInfo.at (consumer), nextRunning, completed);
421+ opInfo->orderedProducers .clear ();
422+ for (BranchRunning const &branch : branches)
423+ opInfo->orderedProducers .push_back (branch.lastOp );
424+
425+ // Complete the brInfo for this operation.
426+ size_t maxRunning = opInfo->sizes .running ;
427+ for (BranchRunning const &branch : branches)
428+ maxRunning = std::max (maxRunning, branch.maxRunning );
429+ brInfo.maxRunning = maxRunning;
430+ brInfo.lastResults = opInfo->sizes .results ;
431+ brInfo.lastOp = opInfo->op ;
432+ completed.insert ({opInfo->op , brInfo});
433+ assert (opInfo->orderedProducers .size () == opInfo->operands .size ());
434+
435+ // Continue to all consumers.
436+ for (Operation *consumer : llvm::reverse (opInfo->consumers )) {
437+ BranchRunning nextRunning{.maxRunning = maxRunning,
438+ .lastResults = opInfo->sizes .results ,
439+ .lastOp = opInfo->op };
440+ worklist.emplace_front (&opToInfo.at (consumer), nextRunning);
441+ }
442+ };
443+
444+ worklist.emplace_back (opInfo, brInfo);
445+ while (!worklist.empty ()) {
446+ auto [opInfo, brInfo] = worklist.front ();
447+ worklist.pop_front ();
448+ determineBranchRunningImpl (opInfo, brInfo, completed);
422449 }
423- assert (opInfo->orderedProducers .size () == opInfo->operands .size ());
424450 }
425451
426452 // / Move the operators to the desired lexical order.
427- void moveToOrder (OpInfo const &fwd) { // NOLINT(misc-no-recursion)
428- for (Operation *op : fwd.orderedProducers ) {
429- if (op == currFn)
453+ void moveToOrder (OpInfo const &fwd) {
454+ std::deque<std::pair<Operation *, Operation *>>
455+ worklist; // first is the producer,second is the operation it should be
456+ // moved before
457+ for (Operation *producer : fwd.orderedProducers ) {
458+ worklist.emplace_back (producer, fwd.op );
459+ }
460+ while (!worklist.empty ()) {
461+ auto [producer, beforeOp] = worklist.front ();
462+ worklist.pop_front ();
463+ if (producer == currFn)
430464 continue ; // BlockArguments cannot be moved.
431- OpInfo &visitFwd = opToInfo.at (op );
465+ OpInfo &visitFwd = opToInfo.at (producer );
432466 if (visitFwd.ordered )
433467 continue ;
434468
435- op ->moveBefore (fwd. op );
469+ producer ->moveBefore (beforeOp );
436470 visitFwd.ordered = true ;
437- moveToOrder (visitFwd);
471+ for (Operation *nextProducer : llvm::reverse (visitFwd.orderedProducers )) {
472+ worklist.emplace_front (nextProducer, producer);
473+ }
438474 }
439475 }
440476
@@ -455,7 +491,11 @@ class XTenMinimizeLiveTensorsPass
455491 assert (isa<func::ReturnOp>(returnStmt) &&
456492 " A function must terminate with a return stmt" );
457493 SmallVector<Value> const retVal = returnStmt->getOperands ();
458- OpInfo fwdInfo = {.op = returnStmt, .operands = retVal, .results = {}};
494+ OpInfo fwdInfo = {.op = returnStmt,
495+ .operands = retVal,
496+ .results = {},
497+ .consumers = {},
498+ .orderedProducers = {}};
459499 auto [opFwdIt, succeeded] =
460500 opToInfo.emplace (returnStmt, std::move (fwdInfo));
461501 OpInfo const &retFwd = opFwdIt->second ;
@@ -505,6 +545,8 @@ class XTenMinimizeLiveTensorsPass
505545
506546private:
507547 // / The analysis results for each operation.
548+ // / Be careful when trying to replace with a different map type, as code in
549+ // / this pass relies on stable iterators/references into the map.
508550 std::map<Operation *, OpInfo> opToInfo;
509551 // / The function being analyzed - needed in places to represent
510552 // / BlockArguments.
0 commit comments