[flang-commits] [flang] a562f6a - [flang][FIR] add canonicalization pattern for fir.if returning OPTIONAL (#205353)
via flang-commits
flang-commits at lists.llvm.org
Wed Jun 24 02:35:33 PDT 2026
Author: jeanPerier
Date: 2026-06-24T11:35:28+02:00
New Revision: a562f6a4833eb15d8d2fdbd54c27c595935ff275
URL: https://github.com/llvm/llvm-project/commit/a562f6a4833eb15d8d2fdbd54c27c595935ff275
DIFF: https://github.com/llvm/llvm-project/commit/a562f6a4833eb15d8d2fdbd54c27c595935ff275.diff
LOG: [flang][FIR] add canonicalization pattern for fir.if returning OPTIONAL (#205353)
Lowering is generating patterns when forwarding OPTIONAL in calls that
looks like:
```
%present = fir.is_present %var : (T) -> i1
%if_result = fir.if %present -> (T) {
fir.result %var : T
} else {
%absent = fir.absent T
fir.result %absent : T
}
```
This specific pattern is a no-op and `%var` can be used directly. The
lowering logic that generates such patterns is inside non trivial
compiler code that has to deal with more complex scenarios where the
code inside the fir.if is more complex. Add a FIR pattern to
canonicalize such code to help with later analysis (like aliasing).
Added:
flang/test/Fir/present-absent-if-fold.fir
Modified:
flang/include/flang/Optimizer/Dialect/FIROps.td
flang/lib/Optimizer/Dialect/FIROps.cpp
Removed:
################################################################################
diff --git a/flang/include/flang/Optimizer/Dialect/FIROps.td b/flang/include/flang/Optimizer/Dialect/FIROps.td
index 35a87c29d0cb6..c6e4d1b3b4d11 100644
--- a/flang/include/flang/Optimizer/Dialect/FIROps.td
+++ b/flang/include/flang/Optimizer/Dialect/FIROps.td
@@ -2560,6 +2560,8 @@ def fir_IfOp
"bool":$withElseRegion)>
];
+ let hasCanonicalizer = 1;
+
let extraClassDeclaration = [{
mlir::OpBuilder getThenBodyBuilder() {
assert(!getThenRegion().empty() && "Unexpected empty 'where' region.");
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 1d61989eba6cf..ba047e71d6aa3 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -5690,6 +5690,65 @@ void fir::IfOp::resultToSourceOps(llvm::SmallVectorImpl<mlir::Value> &results,
results.push_back(term->getOperand(resultNum));
}
+// Fold away a fir.if that only forwards an optional argument or returns
+// fir.absent when it is not present:
+//
+// %present = fir.is_present %var : (T) -> i1
+// %r = fir.if %present -> (T) {
+// fir.result %var : T
+// } else {
+// %absent = fir.absent T
+// fir.result %absent : T
+// }
+//
+// The result is always %var: optional arguments already encode presence.
+struct FoldPresentAbsentIfOp : public mlir::OpRewritePattern<fir::IfOp> {
+ using mlir::OpRewritePattern<fir::IfOp>::OpRewritePattern;
+
+ mlir::LogicalResult
+ matchAndRewrite(fir::IfOp ifOp,
+ mlir::PatternRewriter &rewriter) const override {
+ if (ifOp.getNumResults() != 1)
+ return mlir::failure();
+
+ auto isPresentOp = ifOp.getCondition().getDefiningOp<fir::IsPresentOp>();
+ if (!isPresentOp)
+ return mlir::failure();
+
+ mlir::Value optionalVal = isPresentOp.getVal();
+ mlir::Type resultType = ifOp.getResult(0).getType();
+ if (optionalVal.getType() != resultType)
+ return mlir::failure();
+
+ mlir::Block &thenBlock = ifOp.getThenRegion().front();
+ if (thenBlock.getOperations().size() != 1)
+ return mlir::failure();
+ auto thenResult = mlir::dyn_cast<fir::ResultOp>(thenBlock.getTerminator());
+ if (!thenResult || thenResult.getNumOperands() != 1 ||
+ thenResult.getOperand(0) != optionalVal)
+ return mlir::failure();
+
+ if (ifOp.getElseRegion().empty())
+ return mlir::failure();
+ mlir::Block &elseBlock = ifOp.getElseRegion().front();
+ if (elseBlock.getOperations().size() > 2)
+ return mlir::failure();
+ auto elseResult = mlir::dyn_cast<fir::ResultOp>(elseBlock.getTerminator());
+ if (!elseResult || elseResult.getNumOperands() != 1)
+ return mlir::failure();
+ if (!elseResult.getOperand(0).getDefiningOp<fir::AbsentOp>())
+ return mlir::failure();
+
+ rewriter.replaceOp(ifOp, optionalVal);
+ return mlir::success();
+ }
+};
+
+void fir::IfOp::getCanonicalizationPatterns(mlir::RewritePatternSet &patterns,
+ mlir::MLIRContext *context) {
+ patterns.add<FoldPresentAbsentIfOp>(context);
+}
+
//===----------------------------------------------------------------------===//
// BoxOffsetOp
//===----------------------------------------------------------------------===//
diff --git a/flang/test/Fir/present-absent-if-fold.fir b/flang/test/Fir/present-absent-if-fold.fir
new file mode 100644
index 0000000000000..19fd7859f4a23
--- /dev/null
+++ b/flang/test/Fir/present-absent-if-fold.fir
@@ -0,0 +1,66 @@
+// RUN: fir-opt --canonicalize %s | FileCheck %s
+
+// CHECK-LABEL: func.func @fold_present_absent_if_box(
+// CHECK-SAME: %[[VAR:.*]]: !fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>> {
+// CHECK-NOT: fir.if
+// CHECK-NOT: fir.is_present
+// CHECK-NOT: fir.absent
+// CHECK: return %[[VAR]] : !fir.box<!fir.array<?xf32>>
+func.func @fold_present_absent_if_box(
+ %var: !fir.box<!fir.array<?xf32>>) -> !fir.box<!fir.array<?xf32>> {
+ %present = fir.is_present %var : (!fir.box<!fir.array<?xf32>>) -> i1
+ %if_result = fir.if %present -> (!fir.box<!fir.array<?xf32>>) {
+ fir.result %var : !fir.box<!fir.array<?xf32>>
+ } else {
+ %absent = fir.absent !fir.box<!fir.array<?xf32>>
+ fir.result %absent : !fir.box<!fir.array<?xf32>>
+ }
+ return %if_result : !fir.box<!fir.array<?xf32>>
+}
+
+// CHECK-LABEL: func.func @fold_present_absent_if_ref(
+// CHECK-SAME: %[[VAR:.*]]: !fir.ref<i32>) -> !fir.ref<i32> {
+// CHECK-NOT: fir.if
+// CHECK: return %[[VAR]] : !fir.ref<i32>
+func.func @fold_present_absent_if_ref(%var: !fir.ref<i32>) -> !fir.ref<i32> {
+ %present = fir.is_present %var : (!fir.ref<i32>) -> i1
+ %if_result = fir.if %present -> (!fir.ref<i32>) {
+ fir.result %var : !fir.ref<i32>
+ } else {
+ %absent = fir.absent !fir.ref<i32>
+ fir.result %absent : !fir.ref<i32>
+ }
+ return %if_result : !fir.ref<i32>
+}
+
+func.func private @side_effect() -> ()
+
+// CHECK-LABEL: func.func @no_fold_call_in_then(
+// CHECK: fir.is_present
+// CHECK: fir.if
+func.func @no_fold_call_in_then(%var: !fir.ref<i32>) -> !fir.ref<i32> {
+ %present = fir.is_present %var : (!fir.ref<i32>) -> i1
+ %if_result = fir.if %present -> (!fir.ref<i32>) {
+ fir.call @side_effect() : () -> ()
+ fir.result %var : !fir.ref<i32>
+ } else {
+ %absent = fir.absent !fir.ref<i32>
+ fir.result %absent : !fir.ref<i32>
+ }
+ return %if_result : !fir.ref<i32>
+}
+
+// CHECK-LABEL: func.func @no_fold_call_in_else(
+// CHECK: fir.is_present
+// CHECK: fir.if
+func.func @no_fold_call_in_else(%var: !fir.ref<i32>) -> !fir.ref<i32> {
+ %present = fir.is_present %var : (!fir.ref<i32>) -> i1
+ %if_result = fir.if %present -> (!fir.ref<i32>) {
+ fir.result %var : !fir.ref<i32>
+ } else {
+ %absent = fir.absent !fir.ref<i32>
+ fir.call @side_effect() : () -> ()
+ fir.result %absent : !fir.ref<i32>
+ }
+ return %if_result : !fir.ref<i32>
+}
More information about the flang-commits
mailing list