[PATCH] D72223: [MLIR] Introduce affine.execute_region op
Alex Zinenko via Phabricator via llvm-commits
llvm-commits at lists.llvm.org
Fri Apr 24 06:59:41 PDT 2020
ftynse requested changes to this revision.
ftynse added a comment.
This revision now requires changes to proceed.
Herald added a subscriber: Kayjukh.
I am very sorry, for some reason this diff was not appearing on my phabricator todo list until recently.... Please do not hesitate to ping me by email if I take more than a couple of workdays to iterate.
In general, this change makes sense to me and looks like a proper extension of the affine modeling. My only design concern is that one can circumvent the explicit-capture mechanism for memrefs. A direct solution would be to disallow any values of memref type to be defined in the region, but I have not considered the implications of this on the expressiveness.
There are several places where some action is performed twice (e.g., parsing attribute dict), requesting change for these.
================
Comment at: mlir/include/mlir/Dialect/Affine/IR/AffineOps.td:630-631
+ blocks. The op's region can have zero or more arguments, each of which can
+ only be a memref. The operands bind 1:1 to its region's arguments. The op
+ can't use any memrefs defined outside of it, but can use any other SSA
+ values that dominate it. Its region's blocks can have terminators the same
----------------
I understand the idea of the restriction, but it looks like it can be circumvented by:
```
%0 = ... : memref<...>
%1 = cheat.wrap %0 : memref<...> -> !cheat.opaque
affine.execute_region {
// This defines the memref inside the region, so seemingly complies with the semantics.
%2 = cheat.unwrap %1 : !cheat.opaque -> memref<...>
affine.load %2[...]
}
```
================
Comment at: mlir/include/mlir/Dialect/Affine/IR/AffineOps.td:632-635
+ values that dominate it. Its region's blocks can have terminators the same
+ way as current MLIR functions (FuncOp) can. Control from any return ops
+ from the top level of its region returns to right after the
+ affine.execute_region op. Its control flow thus conforms to the control
----------------
I would avoid referring FuncOp, it is not special in any sense, and it actually allows any terminator. Neither would I remind that blocks must have terminators, it's a core IR requirement.
"returns to right after the affine.execute_region op" sounds unnecessarily complex to me. "std.return" terminator placed inside blocks of the "affine.execute_region" returns the control flow to the "affine.execute_region". Since we already mentioned that "execution_region" executes the region exactly ones, it is naturally implied that the control flow will be further transferred to the control-flow successor of "execution_region...
================
Comment at: mlir/include/mlir/Dialect/Affine/IR/AffineOps.td:656
+ affine.for %i = 0 to %n {
+ affine.execute_region [] = () : () -> () {
+ // %pow can now be used as a loop bound.
----------------
Syntax nit: I'd consider omitting empty `[] = ()`, looks distracting
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:108
+static bool isTopLevelValue(Value value, Operation *opWithRegion) {
+ assert(opWithRegion->getNumRegions() > 0 &&
+ "only to be called on ops with regions");
----------------
Nit: wouldn't it be easier to accept the region as argument instead of the operation that contains it?
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:121
+static Operation *getAffineScope(Operation *op) {
+ // TODO: make this compact by introducing a variadic pack on getParentOfType.
+ auto *curOp = op;
----------------
Would variadic template help you? `isKnownIsolatedFromAbove` isn't a class...
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:128
+
+ assert(false && "op doesn't have a parent op");
+ return nullptr;
----------------
The assert message is misleading. The op may have parent ops, just none that satisfy the "affine scope" conditions.
Also, use `llvm_unreachable` instead of `assert(false)` and drop the return value
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:166-167
+ if (!op) {
+ // This value has to be a block argument for a FuncOp, affine.for,
+ // affine.parallel, or affine.polyfor.
+ auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp();
----------------
I cannot relate this comment to the code below.
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:169
+ auto *parentOp = value.cast<BlockArgument>().getOwner()->getParentOp();
+ return isa<AffineForOp>(parentOp) || isa<AffineParallelOp>(parentOp);
+ }
----------------
Shouldn't this also check for "KnownIsolatedFromAbove" ?
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:1068
+ auto *scope = getAffineScope(*this);
for (auto idx : getSrcIndices()) {
----------------
Nit: we tend to use `auto` when it increases readability, `Operation *` would look just fine here
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2290
+void AffineExecuteRegionOp::build(Builder *builder, OperationState &result,
+ ArrayRef<Value> memrefs) {
+ // Create a region and an empty entry block. The arguments of the region are
----------------
Prefer ValueRange to ArrayRef<Value>
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2299
+ memrefTypes.reserve(memrefs.size());
+ for (auto v : memrefs) {
+ memrefTypes.push_back(v.getType());
----------------
Nit: drop trivial braces
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2302
+ }
+ body->addArguments(memrefTypes);
+ region->push_back(body);
----------------
If you have ValueRange, this entire vector manipulation gets replaced by `memrefs.getTypes()`
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2303
+ body->addArguments(memrefTypes);
+ region->push_back(body);
+
----------------
You already pushed it in line 2295
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2339
+
+ for (auto &argEn : llvm::enumerate(entryBlock.getArguments())) {
+ if (op.getOperand(argEn.index()).getType() != argEn.value().getType())
----------------
Avoid capturing `llvm::enumerate` by-reference. An enumerator only keeps iterators, so taking copying it is cheap, and we avoid running into potential problems with implicit lifetime extension
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2341
+ if (op.getOperand(argEn.index()).getType() != argEn.value().getType())
+ return op.emitOpError("type of one or more region arguments does not "
+ "match corresponding operand");
----------------
Since you already have an enumerator, you might as well mention the position of the mismatching argument.
================
Comment at: mlir/lib/Dialect/Affine/IR/AffineOps.cpp:2404
+ // Parse the optional attribute list.
+ if (parser.parseOptionalAttrDict(result.attributes))
+ return failure();
----------------
You already parser the attr dict 4 lines above.
Repository:
rG LLVM Github Monorepo
CHANGES SINCE LAST ACTION
https://reviews.llvm.org/D72223/new/
https://reviews.llvm.org/D72223
More information about the llvm-commits
mailing list