Enzyme main
Loading...
Searching...
No Matches
SplitMultiResults.cpp
Go to the documentation of this file.
1//===- SplitMultiResults.cpp - Split multi-result region ops --------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8//
9// This file implements a pass that splits region control flow that produces
10// multiple results into separate operations for each result.
11//===----------------------------------------------------------------------===//
12
13#include "Passes/Passes.h"
14#include "mlir/Dialect/SCF/IR/SCF.h"
15
16#include "mlir/Transforms/GreedyPatternRewriteDriver.h"
17
18namespace mlir {
19namespace enzyme {
20#define GEN_PASS_DEF_SPLITMULTIRESULTSPASS
21#include "Passes/Passes.h.inc"
22} // namespace enzyme
23} // namespace mlir
24
25using namespace mlir;
26namespace {
27
28// Given a block terminator, determine if the terminator's operands are solely
29// computed by pure operations within the block.
30static bool allComputedByPureOps(Operation *terminator) {
31 Block *parent = terminator->getBlock();
32 DenseSet<Value> visited;
33 visited.insert_range(terminator->getOperands());
34 SmallVector<Value> frontier(terminator->getOperands());
35 while (!frontier.empty()) {
36 Value curr = frontier.pop_back_val();
37 Operation *definingOp = curr.getDefiningOp();
38 if (!definingOp || definingOp->getBlock() != parent)
39 continue;
40
41 if (!mlir::isPure(definingOp))
42 return false;
43
44 for (Value operand : definingOp->getOperands()) {
45 if (!visited.contains(operand)) {
46 visited.insert(operand);
47 frontier.push_back(operand);
48 }
49 }
50 }
51
52 return true;
53}
54struct SplitMultiIf : public mlir::OpRewritePattern<scf::IfOp> {
55 using OpRewritePattern::OpRewritePattern;
56
57 LogicalResult matchAndRewrite(scf::IfOp ifOp,
58 PatternRewriter &rewriter) const override {
59 if (ifOp.getNumResults() < 2)
60 return failure();
61 if (llvm::all_of(ifOp.getResults(),
62 [](OpResult res) { return res.use_empty(); })) {
63 return failure();
64 }
65 // ignore nested ifs
66 if (llvm::any_of(ifOp.getRegions(), [](Region *region) {
67 return llvm::any_of(region->getOps(), [](Operation &op) {
68 return op.getNumRegions() != 0;
69 });
70 })) {
71 return failure();
72 }
73 // all operands should be computed by pure operations.
74 if (!(allComputedByPureOps(ifOp.thenYield()) &&
75 allComputedByPureOps(ifOp.elseYield()))) {
76 return failure();
77 }
78
79 for (OpResult ifRes : ifOp.getResults()) {
80 auto newIf = scf::IfOp::create(rewriter, ifOp.getLoc(), ifRes.getType(),
81 ifOp.getCondition(),
82 /*withElseRegion=*/true);
83 IRMapping map;
84 OpBuilder::InsertionGuard guard(rewriter);
85 for (auto [oldReg, newReg] :
86 llvm::zip(ifOp.getRegions(), newIf.getRegions())) {
87 for (auto &&[oldBlk, newBlk] : llvm::zip(*oldReg, *newReg)) {
88 rewriter.setInsertionPointToStart(&newBlk);
89 for (auto &op : oldBlk.without_terminator()) {
90 if (mlir::isPure(&op))
91 rewriter.clone(op, map);
92 }
93 scf::YieldOp::create(
94 rewriter, ifOp.getLoc(),
95 map.lookupOrDefault(
96 oldBlk.getTerminator()->getOperand(ifRes.getResultNumber())));
97 }
98 }
99 rewriter.replaceAllUsesWith(ifRes, newIf.getResult(0));
100 }
101 return success();
102 }
103};
104
105struct SplitMultiResultsPass
106 : public enzyme::impl::SplitMultiResultsPassBase<SplitMultiResultsPass> {
107 void runOnOperation() override {
108 RewritePatternSet patterns(&getContext());
109 patterns.insert<SplitMultiIf>(&getContext());
110
111 GreedyRewriteConfig config;
112 config.setRegionSimplificationLevel(GreedySimplifyRegionLevel::Normal);
113 (void)applyPatternsGreedily(getOperation(), std::move(patterns), config);
114 }
115};
116} // namespace