Skip to content

Commit 88f76ae

Browse files
committed
New for loop design.
1 parent fcce5f1 commit 88f76ae

15 files changed

Lines changed: 287 additions & 58 deletions

File tree

compiler/include/graphalg/GraphAlgOps.td

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -403,6 +403,70 @@ def BroadcastOp : Core_Op<"broadcast", [
403403
let hasVerifier = 1;
404404
}
405405

406+
def ForOp : Core_Op<"for", [
407+
Pure,
408+
AttrSizedOperandSegments,
409+
DeclareOpInterfaceMethods<RegionBranchOpInterface, ["getEntrySuccessorOperands"]>]> {
410+
let summary = "For loop with dynamic bounds";
411+
412+
let description = [{
413+
A loop iterating over one of three ranges:
414+
1) `dynBegin` (inclusive) to `dynEnd` (exclusive)
415+
2) `begin` to `begin` + `iters`, where `iters` is an integer
416+
3) `begin` to `begin` + `iters`, where `iters` is a matrix dimension
417+
418+
Only instances with range types 2 or 3 are considered part of GraphAlg
419+
Core. Constant propagation is expected to transform range type 1 into
420+
either 2 or 3.
421+
422+
The `body` region is executed once for every value in the integer range
423+
(that value is passed as the first block argument).
424+
At the first iteration of the loop, the other block arguments take the
425+
values of `initArgs`. For subsequent iterations, results from the
426+
previous iteration (produced by `YieldOp`) are taken instead.
427+
The `until` region, if present, is executed after `body`, and produces a
428+
single boolean scalar indicating whether the loop should terminate
429+
early.
430+
431+
In more imperative terms, `initArgs` can be seen as the set of variables
432+
that are updated in the loop body.
433+
Within the loop body, those variables can be accessed through the block
434+
arguments, and their updated values are set through `YieldOp`.
435+
Finally, `results` represents the new state of those variables after the
436+
loop terminates.
437+
}];
438+
439+
let arguments = (ins
440+
Variadic<Matrix>:$initArgs,
441+
Optional<I64Scalar>:$dynBegin,
442+
Optional<I64Scalar>:$dynEnd,
443+
OptionalAttr<I64Attr>:$begin,
444+
OptionalAttr<DimAttr>:$iters);
445+
446+
let results = (outs Variadic<Matrix>:$results);
447+
448+
let regions = (region SizedRegion<1>:$body, MaxSizedRegion<1>:$until);
449+
450+
let assemblyFormat = [{
451+
(`dyn_begin` `` `=` `` $dynBegin^)?
452+
(`dyn_end` `` `=` `` $dynEnd^)?
453+
(`begin` `` `=` `` $begin^)?
454+
(`iters` `` `=` `` $iters^)?
455+
`init` `(` $initArgs `)` `:` type($initArgs) `->` type($results) attr-dict
456+
`body` $body
457+
`until` $until
458+
}];
459+
460+
let hasVerifier = 1;
461+
let hasRegionVerifier = 1;
462+
let hasFolder = 1;
463+
464+
let extraClassDeclaration = [{
465+
/** Whether at least one of `dyn_begin` and `dyn_end` is set. */
466+
bool isDynamicRange();
467+
}];
468+
}
469+
406470
// Not core according to spec, but we don't want to unroll in the general case.
407471
def ForConstOp : Core_Op<"for_const", [
408472
Pure,
@@ -483,7 +547,7 @@ def ForDimOp : Core_Op<"for_dim", [
483547
def YieldOp : Core_Op<"yield", [
484548
Pure,
485549
Terminator,
486-
ParentOneOf<["ForConstOp", "ForDimOp"]>,
550+
ParentOneOf<["ForOp", "ForConstOp", "ForDimOp"]>,
487551
DeclareOpInterfaceMethods<RegionBranchTerminatorOpInterface>]> {
488552
let summary = "Yield from a loop body";
489553

compiler/src/graphalg/GraphAlgCanonicalize.cpp

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,39 @@ mlir::OpFoldResult BroadcastOp::fold(FoldAdaptor adaptor) {
242242
return nullptr;
243243
}
244244

245+
mlir::LogicalResult
246+
ForOp::fold(FoldAdaptor adaptor,
247+
::llvm::SmallVectorImpl<::mlir::OpFoldResult> &results) {
248+
if (!getBegin() && adaptor.getDynBegin()) {
249+
// Can infer a constant begin to the range.
250+
auto begin = llvm::cast<mlir::IntegerAttr>(adaptor.getDynBegin());
251+
setBeginAttr(begin);
252+
getDynBeginMutable().clear();
253+
return mlir::success();
254+
}
255+
256+
if (!getIters() && getBegin() && adaptor.getDynEnd()) {
257+
// Can infer a constant number of iterations.
258+
auto begin = *getBegin();
259+
auto end = llvm::cast<mlir::IntegerAttr>(adaptor.getDynEnd())
260+
.getValue()
261+
.getZExtValue();
262+
// If end < begin, drop to 0 iterations.
263+
std::size_t iters = 0;
264+
if (begin < end) {
265+
iters = end - begin;
266+
}
267+
268+
// NOTE: number of iterations is encoded as a DimAttr.
269+
auto dim = DimAttr::getConcrete(getContext(), iters);
270+
setItersAttr(dim);
271+
getDynEndMutable().clear();
272+
return mlir::success();
273+
}
274+
275+
return mlir::failure();
276+
}
277+
245278
static mlir::LogicalResult forDimConst(ForDimOp op,
246279
mlir::PatternRewriter &rewriter) {
247280
if (!op.getDim().isConcrete()) {

compiler/src/graphalg/GraphAlgOps.cpp

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -505,6 +505,41 @@ mlir::LogicalResult BroadcastOp::verify() {
505505
return mlir::success();
506506
}
507507

508+
// === ForOp ===
509+
mlir::LogicalResult ForOp::verify() {
510+
if (getDynBegin() && getBegin()) {
511+
return emitOpError("begin and dyn_begin are mutually exclusive");
512+
} else if (!getDynBegin() && !getBegin()) {
513+
return emitOpError("no loop start: must have 'begin' or 'dyn_begin'");
514+
}
515+
516+
if (getDynEnd() && getIters()) {
517+
return emitOpError("begin and iters are mutually exclusive");
518+
} else if (!getDynEnd() && !getIters()) {
519+
return emitOpError("no loop end: must have 'iters' or 'dyn_end'");
520+
}
521+
522+
return mlir::success();
523+
}
524+
525+
mlir::LogicalResult ForOp::verifyRegions() {
526+
return verifyLoop(getOperation(), getInitArgs(), getBody(), getUntil());
527+
}
528+
529+
void ForOp::getSuccessorRegions(
530+
mlir::RegionBranchPoint point,
531+
llvm::SmallVectorImpl<mlir::RegionSuccessor> &regions) {
532+
getLoopSuccessorRegions(getOperation(), getBody(), getUntil(), point,
533+
regions);
534+
}
535+
536+
mlir::OperandRange
537+
ForOp::getEntrySuccessorOperands(mlir::RegionBranchPoint point) {
538+
return getInitArgs();
539+
}
540+
541+
bool ForOp::isDynamicRange() { return getDynBegin() || getDynEnd(); }
542+
508543
// === ForConstOp ===
509544
mlir::LogicalResult ForConstOp::verifyRegions() {
510545
return verifyLoop(getOperation(), getInitArgs(), getBody(), getUntil());

compiler/src/graphalg/GraphAlgSetDimensions.cpp

Lines changed: 31 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -125,20 +125,6 @@ class DimConversionPattern : public mlir::ConversionPattern {
125125
mlir::ConversionPatternRewriter &rewriter) const override;
126126
};
127127

128-
/** Template for rewrites (without type conversion). */
129-
template <typename T>
130-
class DimOpRewritePattern : public mlir::OpRewritePattern<T> {
131-
private:
132-
const DimMapper &_mapper;
133-
134-
mlir::LogicalResult
135-
matchAndRewrite(T op, mlir::PatternRewriter &rewriter) const override;
136-
137-
public:
138-
DimOpRewritePattern(const DimMapper &mapper, mlir::MLIRContext *ctx)
139-
: mlir::OpRewritePattern<T>(ctx), _mapper(mapper) {}
140-
};
141-
142128
} // namespace
143129

144130
mlir::FailureOr<DimMapper>
@@ -309,31 +295,33 @@ mlir::LogicalResult DimConversionPattern::matchAndRewrite(
309295
return mlir::success();
310296
}
311297

312-
template <>
313-
mlir::LogicalResult DimOpRewritePattern<CastDimOp>::matchAndRewrite(
314-
CastDimOp op, mlir::PatternRewriter &rewriter) const {
315-
auto dim = _mapper.convertAttr(op.getInput());
316-
if (!dim) {
317-
return mlir::failure();
298+
static mlir::LogicalResult updateDim(ForOp op, DimMapper &mapper) {
299+
if (!op.getIters() || !op.getIters()->isAbstract()) {
300+
// No update needed
301+
return mlir::success();
318302
}
319303

320-
// The folder on CastDimOp should turn this into a constant.
321-
auto newOp = rewriter.createOrFold<CastDimOp>(op->getLoc(), dim);
322-
rewriter.replaceOp(op, newOp);
304+
auto dim = mapper.convertAttr(*op.getIters());
305+
if (!dim) {
306+
return op.emitOpError("no mapping for ") << *op.getIters();
307+
}
323308

309+
op.setItersAttr(dim);
324310
return mlir::success();
325311
}
326312

327-
template <>
328-
mlir::LogicalResult DimOpRewritePattern<ForDimOp>::matchAndRewrite(
329-
ForDimOp op, mlir::PatternRewriter &rewriter) const {
330-
auto dim = _mapper.convertAttr(op.getDim());
331-
if (!dim) {
332-
return mlir::failure();
313+
static mlir::LogicalResult updateDim(CastDimOp op, DimMapper &mapper) {
314+
if (!op.getInput().isAbstract()) {
315+
// No update needed
316+
return mlir::success();
333317
}
334318

335-
rewriter.modifyOpInPlace(op, [&]() { op.setDimAttr(dim); });
319+
auto dim = mapper.convertAttr(op.getInput());
320+
if (!dim) {
321+
return op.emitOpError("no mapping for ") << op.getInput();
322+
}
336323

324+
op.setInputAttr(dim);
337325
return mlir::success();
338326
}
339327

@@ -356,6 +344,19 @@ void GraphAlgSetDimensions::runOnOperation() {
356344
return signalPassFailure();
357345
}
358346

347+
// Update direct references to dimensions.
348+
bool failedDirectUpdate = false;
349+
func->walk([&](ForOp op) {
350+
if (mlir::failed(updateDim(op, *dimMapper))) {
351+
failedDirectUpdate = true;
352+
}
353+
});
354+
func->walk([&](CastDimOp op) {
355+
if (mlir::failed(updateDim(op, *dimMapper))) {
356+
failedDirectUpdate = true;
357+
}
358+
});
359+
359360
mlir::ConversionTarget target(getContext());
360361
target.addDynamicallyLegalDialect<GraphAlgDialect>(
361362
doesNotUseAbstractDimensions);
@@ -374,12 +375,6 @@ void GraphAlgSetDimensions::runOnOperation() {
374375
// Convert all result types and block argument types.
375376
patterns.add<DimConversionPattern>(typeConverter, &getContext());
376377

377-
// Convert ops that have a special dependency on DimAttr.
378-
patterns.add<DimOpRewritePattern<CastDimOp>, DimOpRewritePattern<ForDimOp>>(
379-
*dimMapper, &getContext());
380-
// Use the canonicalization pattern to rewrite ForDimOp into ForConstOp.
381-
ForDimOp::getCanonicalizationPatterns(patterns, &getContext());
382-
383378
if (mlir::failed(
384379
mlir::applyPartialConversion(func, target, std::move(patterns)))) {
385380
return signalPassFailure();

compiler/src/graphalg/GraphAlgToCore.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,8 @@ void GraphAlgToCore::runOnOperation() {
254254
target.addIllegalDialect<graphalg::GraphAlgDialect>();
255255
target.addDynamicallyLegalDialect<graphalg::GraphAlgDialect>(
256256
[](mlir::Operation *op) { return op->hasTrait<IsCore>(); });
257+
target.addDynamicallyLegalOp<ForOp>(
258+
[](ForOp op) { return !op.isDynamicRange(); });
257259

258260
mlir::RewritePatternSet patterns(&getContext());
259261
patterns.add(convertVecMatMul);
@@ -266,6 +268,20 @@ void GraphAlgToCore::runOnOperation() {
266268
patterns.add(convertTriu);
267269
patterns.add(convertLiteral);
268270

271+
// Conversion will give very unclear errors about dynamic range for loops, so
272+
// do our own analysis first.
273+
bool haveDynamicRangeLoops = false;
274+
getOperation()->walk([&](ForOp op) {
275+
if (op.isDynamicRange()) {
276+
op.emitOpError("loop bound must be a constant in GraphAlg Core");
277+
haveDynamicRangeLoops = true;
278+
}
279+
});
280+
281+
if (haveDynamicRangeLoops) {
282+
return signalPassFailure();
283+
}
284+
269285
if (mlir::failed(mlir::applyFullConversion(getOperation(), target,
270286
std::move(patterns)))) {
271287
signalPassFailure();

0 commit comments

Comments
 (0)