Automated rollback of commit cc202b8a7099f3523b079e204f2cdf3d51c404cc
PiperOrigin-RevId: 275461067
diff --git a/bindings/python/pybind.cpp b/bindings/python/pybind.cpp
index cc8fc6e..258455d 100644
--- a/bindings/python/pybind.cpp
+++ b/bindings/python/pybind.cpp
@@ -306,7 +306,7 @@
PythonValueHandle enter() {
ValueHandle iv(lb.value.getType());
- builder = new LoopBuilder(&iv, lb.value, ub.value, step);
+ builder = new AffineLoopNestBuilder(&iv, lb.value, ub.value, step);
return iv;
}
@@ -318,7 +318,7 @@
PythonValueHandle lb, ub;
int64_t step;
- LoopBuilder *builder = nullptr;
+ AffineLoopNestBuilder *builder = nullptr;
};
struct PythonLoopNestContext {
diff --git a/g3doc/EDSC.md b/g3doc/EDSC.md
index dfbbce2..264dcaf 100644
--- a/g3doc/EDSC.md
+++ b/g3doc/EDSC.md
@@ -40,8 +40,8 @@
## LoopBuilder and AffineLoopNestBuilder
-`mlir::edsc::AffineLoopNestBuilder` provides an interface to allow writing concise and
-structured loop nests.
+`mlir::edsc::AffineLoopNestBuilder` provides an interface to allow writing
+concise and structured loop nests.
```c++
ScopedContext scope(f.get());
@@ -53,10 +53,10 @@
f13(constant_float(llvm::APFloat(13.0f), f32Type)),
i7(constant_int(7, 32)),
i13(constant_int(13, 32));
- LoopBuilder(&i, lb, ub, 3)([&]{
+ AffineLoopNestBuilder(&i, lb, ub, 3)([&]{
lb * index_t(3) + ub;
lb + index_t(3);
- LoopBuilder(&j, lb, ub, 2)([&]{
+ AffineLoopNestBuilder(&j, lb, ub, 2)([&]{
ceilDiv(index_t(31) * floorDiv(i + j * index_t(3), index_t(32)),
index_t(32));
((f7 + f13) / f7) % f13 - f7 * f13;
@@ -99,10 +99,10 @@
``` {.mlir}
// CHECK-LABEL: func @t1(%lhs: memref<3x4x5x6xvector<4xi8>>, %rhs: memref<3x4x5x6xvector<4xi8>>, %result: memref<3x4x5x6xvector<4xi8>>) -> () {
-// CHECK: for {{.*}} = 0 to 3 {
-// CHECK: for {{.*}} = 0 to 4 {
-// CHECK: for {{.*}} = 0 to 5 {
-// CHECK: for {{.*}}= 0 to 6 {
+// CHECK: affine.for {{.*}} = 0 to 3 {
+// CHECK: affine.for {{.*}} = 0 to 4 {
+// CHECK: affine.for {{.*}} = 0 to 5 {
+// CHECK: affine.for {{.*}}= 0 to 6 {
// CHECK: {{.*}} = load %arg1[{{.*}}] : memref<3x4x5x6xvector<4xi8>>
// CHECK: {{.*}} = load %arg0[{{.*}}] : memref<3x4x5x6xvector<4xi8>>
// CHECK: {{.*}} = addi {{.*}} : vector<4xi8>
@@ -119,6 +119,9 @@
// CHECK: store {{.*}}, %arg2[] : memref<f32>
```
+Similar APIs are provided to emit the lower-level `loop.for` op with
+`LoopNestBuilder`. See the `builder-api-test.cpp` test for more usage examples.
+
Since the implementation of declarative builders is in C++, it is also available
to program the IR with an embedded-DSL flavor directly integrated in MLIR. We
make use of these properties in the tutorial.
diff --git a/include/mlir/EDSC/Builders.h b/include/mlir/EDSC/Builders.h
index 9fbca89..5a80571 100644
--- a/include/mlir/EDSC/Builders.h
+++ b/include/mlir/EDSC/Builders.h
@@ -24,6 +24,7 @@
#define MLIR_EDSC_BUILDERS_H_
#include "mlir/Dialect/AffineOps/AffineOps.h"
+#include "mlir/Dialect/LoopOps/LoopOps.h"
#include "mlir/Dialect/StandardOps/Ops.h"
#include "mlir/Dialect/VectorOps/VectorOps.h"
#include "mlir/IR/Builders.h"
@@ -161,8 +162,14 @@
/// Constructs a new AffineForOp and captures the associated induction
/// variable. A ValueHandle pointer is passed as the first argument and is the
/// *only* way to capture the loop induction variable.
- LoopBuilder(ValueHandle *iv, ArrayRef<ValueHandle> lbHandles,
- ArrayRef<ValueHandle> ubHandles, int64_t step);
+ static LoopBuilder makeAffine(ValueHandle *iv,
+ ArrayRef<ValueHandle> lbHandles,
+ ArrayRef<ValueHandle> ubHandles, int64_t step);
+ /// Constructs a new loop::ForOp and captures the associated induction
+ /// variable. A ValueHandle pointer is passed as the first argument and is the
+ /// *only* way to capture the loop induction variable.
+ static LoopBuilder makeLoop(ValueHandle *iv, ValueHandle lbHandle,
+ ValueHandle ubHandle, ValueHandle stepHandle);
LoopBuilder(const LoopBuilder &) = delete;
LoopBuilder(LoopBuilder &&) = default;
@@ -172,7 +179,10 @@
/// The only purpose of this operator is to serve as a sequence point so that
/// the evaluation of `fun` (which build IR snippets in a scoped fashion) is
/// scoped within a LoopBuilder.
- ValueHandle operator()(llvm::function_ref<void(void)> fun = nullptr);
+ void operator()(llvm::function_ref<void(void)> fun = nullptr);
+
+private:
+ LoopBuilder() = default;
};
/// Explicit nested LoopBuilder. Offers a compressed multi-loop builder to avoid
@@ -200,15 +210,34 @@
/// ```
class AffineLoopNestBuilder {
public:
+ // This entry point accomodates the fact that AffineForOp implicitly uses
+ // multiple `lbs` and `ubs` with one single `iv` and `step` to encode `max`
+ // and and `min` constraints respectively.
+ AffineLoopNestBuilder(ValueHandle *iv, ArrayRef<ValueHandle> lbs,
+ ArrayRef<ValueHandle> ubs, int64_t step);
AffineLoopNestBuilder(ArrayRef<ValueHandle *> ivs, ArrayRef<ValueHandle> lbs,
ArrayRef<ValueHandle> ubs, ArrayRef<int64_t> steps);
- ValueHandle operator()(llvm::function_ref<void(void)> fun = nullptr);
+ void operator()(llvm::function_ref<void(void)> fun = nullptr);
private:
SmallVector<LoopBuilder, 4> loops;
};
+/// Helper class to sugar building loop.for loop nests from ranges.
+/// This is similar to edsc::AffineLoopNestBuilder except it operates on
+/// loop.for.
+class LoopNestBuilder {
+public:
+ LoopNestBuilder(llvm::ArrayRef<edsc::ValueHandle *> ivs,
+ ArrayRef<ValueHandle> lbs, ArrayRef<ValueHandle> ubs,
+ ArrayRef<ValueHandle> steps);
+ void operator()(std::function<void(void)> fun = nullptr);
+
+private:
+ llvm::SmallVector<LoopBuilder, 4> loops;
+};
+
// This class exists solely to handle the C++ vexing parse case when
// trying to enter a Block that has already been constructed.
class Append {};
diff --git a/lib/EDSC/Builders.cpp b/lib/EDSC/Builders.cpp
index ba14a14..f1dceec 100644
--- a/lib/EDSC/Builders.cpp
+++ b/lib/EDSC/Builders.cpp
@@ -162,12 +162,12 @@
ubConst.getValue(), step);
}
-mlir::edsc::LoopBuilder::LoopBuilder(ValueHandle *iv,
- ArrayRef<ValueHandle> lbHandles,
- ArrayRef<ValueHandle> ubHandles,
- int64_t step) {
- if (auto res = emitStaticFor(lbHandles, ubHandles, step)) {
- *iv = res.getValue();
+mlir::edsc::LoopBuilder mlir::edsc::LoopBuilder::makeAffine(
+ ValueHandle *iv, ArrayRef<ValueHandle> lbHandles,
+ ArrayRef<ValueHandle> ubHandles, int64_t step) {
+ mlir::edsc::LoopBuilder result;
+ if (auto staticFor = emitStaticFor(lbHandles, ubHandles, step)) {
+ *iv = staticFor.getValue();
} else {
SmallVector<Value *, 4> lbs(lbHandles.begin(), lbHandles.end());
SmallVector<Value *, 4> ubs(ubHandles.begin(), ubHandles.end());
@@ -177,11 +177,24 @@
step);
}
auto *body = getForInductionVarOwner(iv->getValue()).getBody();
- enter(body, /*prev=*/1);
+ result.enter(body, /*prev=*/1);
+ return result;
}
-ValueHandle
-mlir::edsc::LoopBuilder::operator()(llvm::function_ref<void(void)> fun) {
+mlir::edsc::LoopBuilder
+mlir::edsc::LoopBuilder::makeLoop(ValueHandle *iv, ValueHandle lbHandle,
+ ValueHandle ubHandle,
+ ValueHandle stepHandle) {
+ mlir::edsc::LoopBuilder result;
+ auto forOp =
+ OperationHandle::createOp<loop::ForOp>(lbHandle, ubHandle, stepHandle);
+ *iv = ValueHandle(forOp.getInductionVar());
+ auto *body = loop::getForInductionVarOwner(iv->getValue()).getBody();
+ result.enter(body, /*prev=*/1);
+ return result;
+}
+
+void mlir::edsc::LoopBuilder::operator()(llvm::function_ref<void(void)> fun) {
// Call to `exit` must be explicit and asymmetric (cannot happen in the
// destructor) because of ordering wrt comma operator.
/// The particular use case concerns nested blocks:
@@ -203,7 +216,12 @@
if (fun)
fun();
exit();
- return ValueHandle::null();
+}
+
+mlir::edsc::AffineLoopNestBuilder::AffineLoopNestBuilder(
+ ValueHandle *iv, ArrayRef<ValueHandle> lbs, ArrayRef<ValueHandle> ubs,
+ int64_t step) {
+ loops.emplace_back(LoopBuilder::makeAffine(iv, lbs, ubs, step));
}
mlir::edsc::AffineLoopNestBuilder::AffineLoopNestBuilder(
@@ -212,13 +230,12 @@
assert(ivs.size() == lbs.size() && "Mismatch in number of arguments");
assert(ivs.size() == ubs.size() && "Mismatch in number of arguments");
assert(ivs.size() == steps.size() && "Mismatch in number of arguments");
- for (auto it : llvm::zip(ivs, lbs, ubs, steps)) {
- loops.emplace_back(std::get<0>(it), std::get<1>(it), std::get<2>(it),
- std::get<3>(it));
- }
+ for (auto it : llvm::zip(ivs, lbs, ubs, steps))
+ loops.emplace_back(LoopBuilder::makeAffine(
+ std::get<0>(it), std::get<1>(it), std::get<2>(it), std::get<3>(it)));
}
-ValueHandle mlir::edsc::AffineLoopNestBuilder::operator()(
+void mlir::edsc::AffineLoopNestBuilder::operator()(
llvm::function_ref<void(void)> fun) {
if (fun)
fun();
@@ -227,10 +244,32 @@
// to be asymmetric (i.e. enter() occurs on LoopBuilder construction, exit()
// occurs on calling operator()). The asymmetry is required for properly
// nesting imperfectly nested regions (see LoopBuilder::operator()).
- for (auto lit = loops.rbegin(), eit = loops.rend(); lit != eit; ++lit) {
+ for (auto lit = loops.rbegin(), eit = loops.rend(); lit != eit; ++lit)
(*lit)();
+}
+
+mlir::edsc::LoopNestBuilder::LoopNestBuilder(ArrayRef<ValueHandle *> ivs,
+ ArrayRef<ValueHandle> lbs,
+ ArrayRef<ValueHandle> ubs,
+ ArrayRef<ValueHandle> steps) {
+ assert(ivs.size() == lbs.size() && "expected size of ivs and lbs to match");
+ assert(ivs.size() == ubs.size() && "expected size of ivs and ubs to match");
+ assert(ivs.size() == steps.size() &&
+ "expected size of ivs and steps to match");
+ loops.reserve(ivs.size());
+ for (auto it : llvm::zip(ivs, lbs, ubs, steps)) {
+ loops.emplace_back(LoopBuilder::makeLoop(std::get<0>(it), std::get<1>(it),
+ std::get<2>(it), std::get<3>(it)));
}
- return ValueHandle::null();
+ assert(loops.size() == ivs.size() && "Mismatch loops vs ivs size");
+}
+
+void LoopNestBuilder::LoopNestBuilder::operator()(
+ std::function<void(void)> fun) {
+ if (fun)
+ fun();
+ for (auto &lit : reverse(loops))
+ lit({});
}
mlir::edsc::BlockBuilder::BlockBuilder(BlockHandle bh, Append) {
diff --git a/lib/EDSC/CMakeLists.txt b/lib/EDSC/CMakeLists.txt
index 5f3dd9f..967d6ad 100644
--- a/lib/EDSC/CMakeLists.txt
+++ b/lib/EDSC/CMakeLists.txt
@@ -17,6 +17,7 @@
target_link_libraries(MLIREDSC
PUBLIC
MLIRAffineOps
+ MLIRLoopOps
MLIRStandardOps
MLIRTransformUtils
MLIRVectorOps
diff --git a/test/EDSC/CMakeLists.txt b/test/EDSC/CMakeLists.txt
index 2820b90..4c739f8 100644
--- a/test/EDSC/CMakeLists.txt
+++ b/test/EDSC/CMakeLists.txt
@@ -7,8 +7,9 @@
target_link_libraries(mlir-edsc-builder-api-test
PRIVATE
MLIRAffineOps
- MLIRIR
MLIREDSC
+ MLIRIR
+ MLIRLoopOps
MLIRStandardOps
MLIRTransforms
LLVMCore
@@ -19,6 +20,7 @@
whole_archive_link(mlir-edsc-builder-api-test
MLIRAffineOps
+ MLIRLoopOps
MLIRStandardOps
MLIRTransforms
)
diff --git a/test/EDSC/builder-api-test.cpp b/test/EDSC/builder-api-test.cpp
index 73a7366..f08e083 100644
--- a/test/EDSC/builder-api-test.cpp
+++ b/test/EDSC/builder-api-test.cpp
@@ -70,10 +70,10 @@
ValueHandle f13(constant_float(llvm::APFloat(13.0f), f32Type));
ValueHandle i7(constant_int(7, 32));
ValueHandle i13(constant_int(13, 32));
- LoopBuilder(&i, lb, ub, 3)([&] {
+ AffineLoopNestBuilder(&i, lb, ub, 3)([&] {
lb *index_t(3) + ub;
lb + index_t(3);
- LoopBuilder(&j, lb, ub, 2)([&] {
+ AffineLoopNestBuilder(&j, lb, ub, 2)([&] {
ceilDiv(index_t(31) * floorDiv(i + j * index_t(3), index_t(32)),
index_t(32));
((f7 + f13) / f7) % f13 - f7 *f13;
@@ -118,7 +118,7 @@
ScopedContext scope(builder, f.getLoc());
ValueHandle i(indexType), a(f.getArgument(0)), b(f.getArgument(1)),
c(f.getArgument(2)), d(f.getArgument(3));
- LoopBuilder(&i, a - b, c + d, 2)();
+ AffineLoopNestBuilder(&i, a - b, c + d, 2)();
// clang-format off
// CHECK-LABEL: func @builder_dynamic_for(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index, %{{.*}}: index) {
@@ -130,6 +130,30 @@
f.erase();
}
+TEST_FUNC(builder_loop_for) {
+ using namespace edsc;
+ using namespace edsc::op;
+ using namespace edsc::intrinsics;
+ auto indexType = IndexType::get(&globalContext());
+ auto f = makeFunction("builder_loop_for", {},
+ {indexType, indexType, indexType, indexType});
+
+ OpBuilder builder(f.getBody());
+ ScopedContext scope(builder, f.getLoc());
+ ValueHandle i(indexType), a(f.getArgument(0)), b(f.getArgument(1)),
+ c(f.getArgument(2)), d(f.getArgument(3));
+ LoopNestBuilder(&i, a - b, c + d, a)();
+
+ // clang-format off
+ // CHECK-LABEL: func @builder_loop_for(%{{.*}}: index, %{{.*}}: index, %{{.*}}: index, %{{.*}}: index) {
+ // CHECK-DAG: [[r0:%[0-9]+]] = affine.apply ()[s0, s1] -> (s0 - s1)()[%{{.*}}, %{{.*}}]
+ // CHECK-DAG: [[r1:%[0-9]+]] = affine.apply ()[s0, s1] -> (s0 + s1)()[%{{.*}}, %{{.*}}]
+ // CHECK-NEXT: loop.for %{{.*}} = [[r0]] to [[r1]] step {{.*}} {
+ // clang-format on
+ f.print(llvm::outs());
+ f.erase();
+}
+
TEST_FUNC(builder_max_min_for) {
using namespace edsc;
using namespace edsc::op;
@@ -142,7 +166,7 @@
ScopedContext scope(builder, f.getLoc());
ValueHandle i(indexType), lb1(f.getArgument(0)), lb2(f.getArgument(1)),
ub1(f.getArgument(2)), ub2(f.getArgument(3));
- LoopBuilder(&i, {lb1, lb2}, {ub1, ub2}, 1)();
+ AffineLoopNestBuilder(&i, {lb1, lb2}, {ub1, ub2}, 1)();
ret();
// clang-format off
@@ -344,10 +368,10 @@
ub2 = vA.ub(2);
step2 = vA.step(2);
AffineLoopNestBuilder({&i, &j}, {lb0, lb1}, {ub0, ub1}, {step0, step1})([&]{
- LoopBuilder(&k1, lb2, ub2, step2)([&]{
+ AffineLoopNestBuilder(&k1, lb2, ub2, step2)([&]{
C(i, j, k1) = f7 + A(i, j, k1) + B(i, j, k1);
});
- LoopBuilder(&k2, lb2, ub2, step2)([&]{
+ AffineLoopNestBuilder(&k2, lb2, ub2, step2)([&]{
C(i, j, k2) += A(i, j, k2) + B(i, j, k2);
});
});
@@ -699,7 +723,7 @@
IndexHandle i, N(vC.ub(0));
// clang-format off
- LoopBuilder(&i, zero, N, 1)([&]{
+ AffineLoopNestBuilder(&i, zero, N, 1)([&]{
C((ValueHandle)D(i)) = A((ValueHandle)B(i));
});
// clang-format on
@@ -733,7 +757,7 @@
IndexHandle iv;
// clang-format off
- LoopBuilder(&iv, zero, one, 1)([&]{
+ AffineLoopNestBuilder(&iv, zero, one, 1)([&]{
res() = input();
});
// clang-format on