|
24 | 24 | #include "flang/Optimizer/Builder/Todo.h"
|
25 | 25 | #include "flang/Optimizer/Dialect/Support/FIRContext.h"
|
26 | 26 | #include "flang/Optimizer/HLFIR/Passes.h"
|
| 27 | +#include "mlir/Analysis/Liveness.h" |
27 | 28 | #include "mlir/IR/Dominance.h"
|
28 | 29 | #include "mlir/IR/IRMapping.h"
|
29 | 30 | #include "mlir/Transforms/DialectConversion.h"
|
| 31 | +#include "mlir/Transforms/RegionUtils.h" |
30 | 32 | #include "llvm/ADT/SmallSet.h"
|
31 | 33 | #include "llvm/ADT/TypeSwitch.h"
|
32 | 34 | #include "llvm/Support/Debug.h"
|
| 35 | +#include <unordered_set> |
33 | 36 |
|
34 | 37 | namespace hlfir {
|
35 | 38 | #define GEN_PASS_DEF_LOWERHLFIRORDEREDASSIGNMENTS
|
@@ -263,6 +266,19 @@ class OrderedAssignmentRewriter {
|
263 | 266 | return &inserted.first->second;
|
264 | 267 | }
|
265 | 268 |
|
| 269 | + /// Given a top-level hlfir.where, look for hlfir.exactly_once operations |
| 270 | + /// inside it and see if any of the values live into hlfir.exactly_once |
| 271 | + /// do not dominate hlfir.where. This may happen due to CSE reusing |
| 272 | + /// results of operations from the region parent to hlfir.exactly_once. |
| 273 | + /// Since we are going to clone the body of hlfir.exactly_once before |
| 274 | + /// the top-level hlfir.where, such def-use will cause problems. |
| 275 | + /// There are options how to resolve this in a different way, |
| 276 | + /// e.g. making hlfir.exactly_once IsolatedFromAbove or making |
| 277 | + /// it a region of hlfir.where and wiring the result(s) through |
| 278 | + /// the block arguments. For the time being, this canonicalization |
| 279 | + /// tries to undo the effects of CSE. |
| 280 | + void canonicalizeExactlyOnceInsideWhere(hlfir::WhereOp whereOp); |
| 281 | + |
266 | 282 | fir::FirOpBuilder &builder;
|
267 | 283 |
|
268 | 284 | /// Map containing the mapping between the original order assignment tree
|
@@ -523,6 +539,10 @@ void OrderedAssignmentRewriter::generateMaskIfOp(mlir::Value cdt) {
|
523 | 539 | void OrderedAssignmentRewriter::pre(hlfir::WhereOp whereOp) {
|
524 | 540 | mlir::Location loc = whereOp.getLoc();
|
525 | 541 | if (!whereLoopNest) {
|
| 542 | + // Make sure liveness information is valid for the inner hlfir.exactly_once |
| 543 | + // operations, and their bodies can be cloned before the top-level |
| 544 | + // hlfir.where. |
| 545 | + canonicalizeExactlyOnceInsideWhere(whereOp); |
526 | 546 | // This is the top-level WHERE. Start a loop nest iterating on the shape of
|
527 | 547 | // the where mask.
|
528 | 548 | if (auto maybeSaved = getIfSaved(whereOp.getMaskRegion())) {
|
@@ -1350,6 +1370,105 @@ void OrderedAssignmentRewriter::saveLeftHandSide(
|
1350 | 1370 | }
|
1351 | 1371 | }
|
1352 | 1372 |
|
| 1373 | +void OrderedAssignmentRewriter::canonicalizeExactlyOnceInsideWhere( |
| 1374 | + hlfir::WhereOp whereOp) { |
| 1375 | + auto getDefinition = [](mlir::Value v) { |
| 1376 | + mlir::Operation *op = v.getDefiningOp(); |
| 1377 | + bool isValid = true; |
| 1378 | + if (!op) { |
| 1379 | + LLVM_DEBUG( |
| 1380 | + llvm::dbgs() |
| 1381 | + << "Value live into hlfir.exactly_once has no defining operation: " |
| 1382 | + << v << "\n"); |
| 1383 | + isValid = false; |
| 1384 | + } |
| 1385 | + if (op->getNumRegions() != 0) { |
| 1386 | + LLVM_DEBUG( |
| 1387 | + llvm::dbgs() |
| 1388 | + << "Cannot pull an operation with regions into hlfir.exactly_once" |
| 1389 | + << *op << "\n"); |
| 1390 | + isValid = false; |
| 1391 | + } |
| 1392 | + auto effects = mlir::getEffectsRecursively(op); |
| 1393 | + if (!effects || !effects->empty()) { |
| 1394 | + LLVM_DEBUG(llvm::dbgs() << "Side effects on operation with result live " |
| 1395 | + "into hlfir.exactly_once" |
| 1396 | + << *op << "\n"); |
| 1397 | + isValid = false; |
| 1398 | + } |
| 1399 | + assert(isValid && "invalid live-in"); |
| 1400 | + return op; |
| 1401 | + }; |
| 1402 | + mlir::Liveness liveness(whereOp.getOperation()); |
| 1403 | + whereOp->walk([&](hlfir::ExactlyOnceOp op) { |
| 1404 | + std::unordered_set<mlir::Operation *> liveInSet; |
| 1405 | + LLVM_DEBUG(llvm::dbgs() << "Canonicalizing:\n" << op << "\n"); |
| 1406 | + auto &liveIns = liveness.getLiveIn(&op.getBody().front()); |
| 1407 | + if (liveIns.empty()) |
| 1408 | + return; |
| 1409 | + // Note that the liveIns set is not ordered. |
| 1410 | + for (mlir::Value liveIn : liveIns) { |
| 1411 | + if (!dominanceInfo.properlyDominates(liveIn, whereOp)) { |
| 1412 | + LLVM_DEBUG(llvm::dbgs() |
| 1413 | + << "Does not dominate top-level where: " << liveIn << "\n"); |
| 1414 | + liveInSet.insert(getDefinition(liveIn)); |
| 1415 | + } |
| 1416 | + } |
| 1417 | + |
| 1418 | + // Populate the set of operations that we need to pull into |
| 1419 | + // hlfir.exactly_once, so that the only live-ins left are the ones |
| 1420 | + // that dominate whereOp. |
| 1421 | + std::unordered_set<mlir::Operation *> cloneSet(liveInSet); |
| 1422 | + llvm::SmallVector<mlir::Operation *> workList(cloneSet.begin(), |
| 1423 | + cloneSet.end()); |
| 1424 | + while (!workList.empty()) { |
| 1425 | + mlir::Operation *current = workList.pop_back_val(); |
| 1426 | + for (mlir::Value operand : current->getOperands()) { |
| 1427 | + if (dominanceInfo.properlyDominates(operand, whereOp)) |
| 1428 | + continue; |
| 1429 | + mlir::Operation *def = getDefinition(operand); |
| 1430 | + if (cloneSet.count(def)) |
| 1431 | + continue; |
| 1432 | + cloneSet.insert(def); |
| 1433 | + workList.push_back(def); |
| 1434 | + } |
| 1435 | + } |
| 1436 | + |
| 1437 | + // Sort the operations by dominance. This preserves their order |
| 1438 | + // after the cloning, and also guarantees stable IR generation. |
| 1439 | + llvm::SmallVector<mlir::Operation *> cloneList(cloneSet.begin(), |
| 1440 | + cloneSet.end()); |
| 1441 | + llvm::sort(cloneList, [&](mlir::Operation *L, mlir::Operation *R) { |
| 1442 | + return dominanceInfo.properlyDominates(L, R); |
| 1443 | + }); |
| 1444 | + |
| 1445 | + // Clone the operations. |
| 1446 | + mlir::IRMapping mapper; |
| 1447 | + mlir::Operation::CloneOptions options; |
| 1448 | + options.cloneOperands(); |
| 1449 | + mlir::OpBuilder::InsertionGuard guard(builder); |
| 1450 | + builder.setInsertionPointToStart(&op.getBody().front()); |
| 1451 | + |
| 1452 | + for (auto *toClone : cloneList) { |
| 1453 | + LLVM_DEBUG(llvm::dbgs() << "Cloning: " << *toClone << "\n"); |
| 1454 | + builder.insert(toClone->clone(mapper, options)); |
| 1455 | + } |
| 1456 | + for (mlir::Operation *oldOps : liveInSet) |
| 1457 | + for (mlir::Value oldVal : oldOps->getResults()) { |
| 1458 | + mlir::Value newVal = mapper.lookup(oldVal); |
| 1459 | + if (!newVal) { |
| 1460 | + LLVM_DEBUG(llvm::dbgs() << "No clone found for: " << oldVal << "\n"); |
| 1461 | + assert(false && "missing clone"); |
| 1462 | + } |
| 1463 | + mlir::replaceAllUsesInRegionWith(oldVal, newVal, op.getBody()); |
| 1464 | + } |
| 1465 | + |
| 1466 | + LLVM_DEBUG(llvm::dbgs() << "Finished canonicalization\n"); |
| 1467 | + if (!liveInSet.empty()) |
| 1468 | + LLVM_DEBUG(llvm::dbgs() << op << "\n"); |
| 1469 | + }); |
| 1470 | +} |
| 1471 | + |
1353 | 1472 | /// Lower an ordered assignment tree to fir.do_loop and hlfir.assign given
|
1354 | 1473 | /// a schedule.
|
1355 | 1474 | static void lower(hlfir::OrderedAssignmentTreeOpInterface root,
|
|
0 commit comments