[PATCH] D156889: [mlir][cf] Add ControlFlow to SCF lifting pass

Tobias Gysi via Phabricator via llvm-commits llvm-commits at lists.llvm.org
Thu Aug 3 00:53:50 PDT 2023


gysit added a comment.

Amazing progress!

I did a first pass and the logic makes sense to me. I left a couple of nit and naming comments. The latter can be seen as suggestions, take what you think makes sense.



================
Comment at: mlir/include/mlir/Transforms/CFGToSCF.h:37
+  /// left as is.
+  virtual Operation *createBranchRegionOp(OpBuilder &builder,
+                                          Operation *controlFlowCondOp,
----------------
Would it make sense to prefix all methods that create SCF operation with SCF? E.g., for this one it may make sense to rename to `createSCFBranchOp` and below `createSCFLoopOp`. That way it may be a bit easier to follow in the code when new SCF stuff is created.


================
Comment at: mlir/lib/Conversion/ControlFlowToSCF/ControlFlowToSCF.cpp:136-138
+    // TODO: This should create a `ub.unreachable` op once that exists to make
+    //       the pass independent of the func dialect. For now just return
+    //       poison values.
----------------



================
Comment at: mlir/lib/Conversion/ControlFlowToSCF/ControlFlowToSCF.cpp:171-174
+    if (result.wasInterrupted()) {
+      signalPassFailure();
+      return;
+    }
----------------
ultra nit:


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:91
+//
+// Lifting conditional branches is done by analyzing the first conditional
+// branch encountered in the entry region. The algorithm then identifies
----------------
or some other form of emphasizing the first would be nice since it is pretty important.


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:129-130
+/// Returns the mutable operand range used to transfer operands from `block` to
+/// its successor with the given index. The return range being mutable allows
+/// modifying the operands being transferred.
+static MutableOperandRange
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:190
+
+/// Structure containing all special edges of a cycle. A cycle is a
+/// generalization of a loop that may have multiple entry edges. See also
----------------
I would probably enumerate: Structure that contains the entry, exit, and back edges of a cycle.


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:193
+/// https://llvm.org/docs/CycleTerminology.html.
+struct CyclesEdges {
+  /// All edges from a block outside the cycle to a block inside the cycle.
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:204
+/// Class used to orchestrate creation of so-called edge multiplexers.
+/// This constructs creates a new basic blocks and redirects all edges given to
+/// be routed through that basic block before branching to their original
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:210-212
+  Block *multiplexerBlock;
+  function_ref<Value(unsigned)> getSwitchValue;
+  function_ref<Value(Type)> getUndefValue;
----------------
nit: I would probably put the private member variables at the end of the class definition? Also maybe put an explicit `private:` at the beginning of the class definition?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:234-237
+  /// appropriate block arguments after the first entry block. `extraArgs`
+  /// allows adding additional block arguments after the required ones to the
+  /// multiplexer block, allowing additional successor operands to be passed
+  /// from every edge.
----------------
nit: `extraArgs` contains the types of possible extra block arguments passed to the multiplexer block that are added to the successor operands of  every outgoing edge.



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:257
+    // be assigned the `getUndefValue`. The amount of block arguments and their
+    // offset is saved in the map for `redirectEdges` to transform the edges.
+    llvm::SmallMapVector<Block *, BlockEntry, 4> entries;
----------------
nit: `redirectEdges` == `entries`? Would it make sense to rename to edgeToArgRangeMap, or similar?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:304
+    // successor block.
+    ValueRange destArgs = multiplexerBlock->getArguments();
+    iter = llvm::transform(
----------------
Could we fill the array using an llvm::enumerate and if blocks that check based on the index what we need todo?

Something similar to this:

```
for (auto &it : llvm::enumerate(multiplexerBlock->getArguments()) {
  if (block.offset <= it.index() < block.offset + block.size) {
    // copy args of this block
   continue; 
  }
  if (it.index() == discriminatorIdx) {
    // copy discriminator 
    continue;
  }
  if (it.index() > discriminatorIdx) {
    // copy extra argument
    continue;
  }
  // create an undef value
}
```


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:315
+
+    unsigned remainingArgsSize = discriminator ? 1 : 0;
+    remainingArgsSize += extraArgs.size();
----------------
nit: discriminatorAndExtraArgsSize? The term remaining seems a bit confusing here since I would interpret this as the arguments of the remaining blocks. 


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:348
+    SmallVector<Block *> caseDestinations;
+
+    for (auto &&[index, pair] : llvm::enumerate(entries)) {
----------------
ultra nit: drop newline?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:362
+    // create a dummy flag for the switch.
+    Value realDiscriminator = discriminator && caseArguments.size() > 1
+                                  ? discriminator
----------------
isn't caseArgument.size() > 1 iff discriminator != 0? if yes I would probably simplify to:

```
if (caseArguments.size() == 1)
  discriminator = getSwitchValue(0);
```


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:382
+/// to be equivalent.
+struct ReturnLikeOperationEquivalence : public llvm::DenseMapInfo<Operation *> {
+  static unsigned getHashValue(const Operation *opC) {
----------------
ultra nit: ReturnLikeOpEquivalence or ReturnLikeEquivalence?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:406
+/// every return-like operation.
+class ReturnLikeExitTransformer {
+  // Mapping of return-like operation to block. All return-like operations
----------------
nit: maybe RegionExitCombiner or similar? Transformer sounds a bit generic?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:407-411
+  // Mapping of return-like operation to block. All return-like operations
+  // of the same kind with the same attributes, properties and types are seen as
+  // equivalent. First occurrence seen is kept in the map.
+  llvm::SmallDenseMap<Operation *, Block *, 4, ReturnLikeOperationEquivalence>
+      mapping;
----------------
gysit wrote:
> Let's move the private members to the end of the class definition? The comment could be transformed to a doc string?



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:407-413
+  // Mapping of return-like operation to block. All return-like operations
+  // of the same kind with the same attributes, properties and types are seen as
+  // equivalent. First occurrence seen is kept in the map.
+  llvm::SmallDenseMap<Operation *, Block *, 4, ReturnLikeOperationEquivalence>
+      mapping;
+  Region &topLevelRegion;
+  CFGToSCFInterface &interface;
----------------
Let's move the private members to the end of the class definition? The comment could be transformed to a doc string?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:423
+  /// instance of `returnLikeOperation`s kind.
+  void transform(Operation *returnLikeOperation,
+                 llvm::function_ref<Value(unsigned)> getSwitchValue) {
----------------
nit: maybe rename to combineWithPrevExits or similar?

Also, would it make sense to shorten operation to op for some of the variables, e.g. returnLikeOperation -> returnLikeOp or originalOperation -> originalOp?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:461
+  /// Returns true if any IR transformations have occurred so far.
+  bool changed() const {
+    // IR transformations have occurred if any exit blocks were created.
----------------
nit: hasChanged


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:470
+
+/// Calculates all special edges of the given cycle.
+static CyclesEdges
----------------
nit: -> the entry, exit, and back edges of the given cycle.


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:496-502
+  for (Block *block : cycles)
+    for (auto &&[succIndex, succ] : llvm::enumerate(block->getSuccessors())) {
+      if (!entryBlock.contains(succ))
+        continue;
+
+      result.backEdges.emplace_back(block, succIndex);
+    }
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:549
+             llvm::map_range(backEdges, std::mem_fn(&Edge::getSuccessor))) &&
+         "All repetition arcs must lead to the single loop header");
+
----------------
nit: should we replace arc by edge here and below?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:572-573
+  // Since this is a loop, all back edges point to the same loop header.
+  Edge latchRepr = backEdges.front();
+  Block *loopHeader = latchRepr.getSuccessor();
+
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:751
+
+    CyclesEdges arcs = calculateCycleEdges(cycleBlockSet);
+    Block *loopHeader = arcs.entryEdges.front().getSuccessor();
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:872
+    DominanceInfo &dominanceInfo) {
+  switch (regionEntry->getNumSuccessors()) {
+  case 0:
----------------
nit: I would favor if blocks over switch case in this case:

```
if (regionEntry->getNumSuccessors() == 0) {
  //
}
if (regionEntry->getNumSuccessors() == 1) {
  //
}
```



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:899
+  // determined and become the region following that edge.
+  // The last region is the continuation that follow the branch regions.
+  SmallPtrSet<Block *, 8> notContinuation;
----------------



================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:958
+  // that case the control flow operation must stay as we are unable to create a
+  // single exit-block. We can nevertheless process all its successors as they
+  // are all single-entry regions.
----------------
?

Especially the case three below is quite hard to follow and I am not sure the first sentence matches may understanding of the code. I wonder if examples would be more helpful here than text only?


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:960
+  // are all single-entry regions.
+  // 3) Some branch regions have an edge to one or more continuation.
+  // This occurs if some branches end in the same return-like operation and some
----------------
Reading the code it seems the condition is somewhat more complex:
- Either only a subset has a continuation edge
- Or not all continuation edges point to the same continuation block.
is that correct?

I guess an example may be more intuitive to understand in this case.


================
Comment at: mlir/lib/Transforms/Utils/CFGToSCF.cpp:1171
+
+    size_t sizePrior = workList.size();
+    llvm::append_range(workList, transformCyclesToSCFLoops(
----------------
A comment that explains what the transform functions add to the work lists may make sense here albeit it being a bit repetitive given the function doc strings. 


Repository:
  rG LLVM Github Monorepo

CHANGES SINCE LAST ACTION
  https://reviews.llvm.org/D156889/new/

https://reviews.llvm.org/D156889



More information about the llvm-commits mailing list