Skip to content

Commit bf08d0e

Browse files
authored
[flang] fix cg-rewrite DCE (#99653)
cg-rewrite runs regionDCE to get rid of the unused fir.shape/shift/slice before codegen since those operations have no codegen. I came across an issue where unreachable code would cause the pass to fail with `error: loc(...): null operand found`. It turns out `mlir::RegionDCE` does not work properly in presence of unreachable code because it delete operations in reachable code that are unused in reachable code, but still used in unreachable code (like the constant in the added test case). It seems `mlir::RegionDCE` is always run after `mlir::eraseUnreachableBlock` outside of this pass. A solution could be to run `mlir::eraseUnreachableBlock` here or to try modifying `mlir::RegionDCE`. But the current behavior may be intentional, and both of these calls are actually quite expensive. For instance, RegionDCE will does liveness analysis, and removes unused block arguments, which is way more than what is needed here. I am not very found of having this rather heavy transformation inside this pass (they should be run after or before if they matter in the overall pipeline). Do a naïve backward deletion of the trivially dead operations instead. It is cheaper, and works with unreachable code.
1 parent 102d168 commit bf08d0e

File tree

2 files changed

+35
-2
lines changed

2 files changed

+35
-2
lines changed

flang/lib/Optimizer/CodeGen/PreCGRewrite.cpp

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@
1818
#include "flang/Optimizer/Dialect/FIROps.h"
1919
#include "flang/Optimizer/Dialect/FIRType.h"
2020
#include "flang/Optimizer/Dialect/Support/FIRContext.h"
21+
#include "mlir/IR/Iterators.h"
2122
#include "mlir/Transforms/DialectConversion.h"
22-
#include "mlir/Transforms/RegionUtils.h"
2323
#include "llvm/ADT/STLExtras.h"
2424
#include "llvm/Support/Debug.h"
2525

@@ -325,6 +325,25 @@ class DummyScopeOpConversion
325325
}
326326
};
327327

328+
/// Simple DCE to erase fir.shape/shift/slice/unused shape operands after this
329+
/// pass (fir.shape and like have no codegen).
330+
/// mlir::RegionDCE is expensive and requires running
331+
/// mlir::eraseUnreachableBlocks. It does things that are not needed here, like
332+
/// removing unused block arguments. fir.shape/shift/slice cannot be block
333+
/// arguments.
334+
/// This helper does a naive backward walk of the IR. It is not even guaranteed
335+
/// to walk blocks according to backward dominance, but that is good enough for
336+
/// what is done here, fir.shape/shift/slice have no usages anymore. The
337+
/// backward walk allows getting rid of most of the unused operands, it is not a
338+
/// problem to leave some in the weird cases.
339+
static void simpleDCE(mlir::RewriterBase &rewriter, mlir::Operation *op) {
340+
op->walk<mlir::WalkOrder::PostOrder, mlir::ReverseIterator>(
341+
[&](mlir::Operation *subOp) {
342+
if (mlir::isOpTriviallyDead(subOp))
343+
rewriter.eraseOp(subOp);
344+
});
345+
}
346+
328347
class CodeGenRewrite : public fir::impl::CodeGenRewriteBase<CodeGenRewrite> {
329348
public:
330349
using CodeGenRewriteBase<CodeGenRewrite>::CodeGenRewriteBase;
@@ -356,7 +375,7 @@ class CodeGenRewrite : public fir::impl::CodeGenRewriteBase<CodeGenRewrite> {
356375
}
357376
// Erase any residual (fir.shape, fir.slice...).
358377
mlir::IRRewriter rewriter(&context);
359-
(void)mlir::runRegionDCE(rewriter, mod->getRegions());
378+
simpleDCE(rewriter, mod.getOperation());
360379
}
361380
};
362381

flang/test/Fir/declare-codegen.fir

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,17 @@ func.func @useless_shape_with_duplicate_extent_operand(%arg0: !fir.ref<!fir.arra
3838

3939
// DECL-LABEL: func.func @useless_shape_with_duplicate_extent_operand(
4040
// DECL: fircg.ext_declare
41+
42+
// Test DCE does not crash because of unreachable code.
43+
func.func @unreachable_code(%arg0: !fir.ref<!fir.char<1,10>>) {
44+
%c10 = arith.constant 10 : index
45+
%2 = fir.declare %arg0 typeparams %c10 {uniq_name = "live_code"} : (!fir.ref<!fir.char<1,10>>, index) -> (!fir.ref<!fir.char<1,10>>)
46+
return
47+
^bb2: // no predecessors
48+
%3 = fir.declare %arg0 typeparams %c10 {uniq_name = "dead_code"} : (!fir.ref<!fir.char<1,10>>, index) -> (!fir.ref<!fir.char<1,10>>)
49+
fir.unreachable
50+
}
51+
// NODECL-LABEL: func.func @unreachable_code(
52+
// NODECL-NOT: uniq_name = "live_code"
53+
// DECL-LABEL: func.func @unreachable_code(
54+
// DECL: uniq_name = "live_code"

0 commit comments

Comments
 (0)