[flang-commits] [flang] [flang] preserve representation of logical constants from TRANSFER (PR #192417)

via flang-commits flang-commits at lists.llvm.org
Thu Apr 16 02:53:28 PDT 2026


llvmbot wrote:


<!--LLVM PR SUMMARY COMMENT-->

@llvm/pr-subscribers-flang-fir-hlfir

Author: jeanPerier

<details>
<summary>Changes</summary>

This patch fixes the reproducer from https://github.com/llvm/llvm-project/issues/192234.

Logical constant have canonical representation in flang (0 and 1, which is compiler specific), but other values can be obtained via TRANSFER from integer (in which case they are true if and only if different from zero, which is a compiler specific interpretation).

While using TRANSFER to obtain logical is not recommended because it is not portable, the standard at guarantee that round trips should preserve the value.

This was not the case with constants because lowering was always lowering logical constants to either 0 or 1 even when they are obtained via TRANSFER. This patch fixes this by lowering non canonical logical constants to an integer constant + bitcast.  A folder is added to fold converts to i1 of such bitcast so that usage of such TRANSFER in branches for instance can still be optimized at the MLIR level.

---
Full diff: https://github.com/llvm/llvm-project/pull/192417.diff


4 Files Affected:

- (modified) flang/lib/Lower/ConvertConstant.cpp (+13-2) 
- (modified) flang/lib/Optimizer/Dialect/FIROps.cpp (+13) 
- (modified) flang/test/Fir/bitcast-fold.fir (+20) 
- (added) flang/test/Lower/constant-logical-transfer.f90 (+23) 


``````````diff
diff --git a/flang/lib/Lower/ConvertConstant.cpp b/flang/lib/Lower/ConvertConstant.cpp
index f786673c606fc..0feb78e7fe9a1 100644
--- a/flang/lib/Lower/ConvertConstant.cpp
+++ b/flang/lib/Lower/ConvertConstant.cpp
@@ -63,7 +63,10 @@ static mlir::Attribute convertToAttribute(
                             {value.ToUInt64(), value.SHIFTR(64).ToUInt64()}));
     }
   } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
-    return builder.getIntegerAttr(type, value.IsTrue());
+    if (value.IsCanonical())
+      return builder.getIntegerAttr(type, value.IsTrue());
+    else
+      return builder.getIntegerAttr(type, value.word().ToInt64());
   } else {
     auto getFloatAttr = [&](const auto &value, mlir::Type type) {
       std::string str = value.DumpHexadecimal();
@@ -262,7 +265,15 @@ static mlir::Value genScalarLit(
     }
     return builder.createIntegerConstant(loc, ty, value.ToInt64());
   } else if constexpr (TC == Fortran::common::TypeCategory::Logical) {
-    return builder.createBool(loc, value.IsTrue());
+    if (value.IsCanonical())
+      return builder.createBool(loc, value.IsTrue());
+    mlir::Type logicalType = Fortran::lower::getFIRType(
+        builder.getContext(), Fortran::common::TypeCategory::Logical, KIND, {});
+    mlir::Type intType = Fortran::lower::getFIRType(
+        builder.getContext(), Fortran::common::TypeCategory::Integer, KIND, {});
+    mlir::Value integer =
+        builder.createIntegerConstant(loc, intType, value.word().ToInt64());
+    return fir::BitcastOp::create(builder, loc, logicalType, integer);
   } else if constexpr (TC == Fortran::common::TypeCategory::Real) {
     std::string str = value.DumpHexadecimal();
     if constexpr (KIND == 2) {
diff --git a/flang/lib/Optimizer/Dialect/FIROps.cpp b/flang/lib/Optimizer/Dialect/FIROps.cpp
index 405ada36a0037..4705033945611 100644
--- a/flang/lib/Optimizer/Dialect/FIROps.cpp
+++ b/flang/lib/Optimizer/Dialect/FIROps.cpp
@@ -1694,6 +1694,12 @@ void fir::ConvertOp::getCanonicalizationPatterns(
       context);
 }
 
+static bool isI1(mlir::Type ty) {
+  if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(ty))
+    return intTy.getWidth() == 1;
+  return false;
+}
+
 mlir::OpFoldResult fir::ConvertOp::fold(FoldAdaptor adaptor) {
   if (getValue().getType() == getType())
     return getValue();
@@ -1713,6 +1719,13 @@ mlir::OpFoldResult fir::ConvertOp::fold(FoldAdaptor adaptor) {
             (fromTy.getWidth() == 1))
           return inner.getValue();
   }
+  // (convert (bitcast 'cst : int -> logical) : logical -> i1) ==> `'cst != 0`
+  if (isI1(getType()) &&
+      matchPattern(getValue(), mlir::m_Op<fir::BitcastOp>())) {
+    auto bitcast = mlir::cast<fir::BitcastOp>(getValue().getDefiningOp());
+    if (auto cst = fir::getIntIfConstant(bitcast.getValue()))
+      return mlir::IntegerAttr::get(getType(), cst != 0 ? 1 : 0);
+  }
   return {};
 }
 
diff --git a/flang/test/Fir/bitcast-fold.fir b/flang/test/Fir/bitcast-fold.fir
index 18b7a5e2e3daa..431230ec0d1e9 100644
--- a/flang/test/Fir/bitcast-fold.fir
+++ b/flang/test/Fir/bitcast-fold.fir
@@ -24,3 +24,23 @@ func.func @bitcast_chain_collapse(%x : i32) -> !fir.logical<4> {
   // CHECK-NEXT: return %[[RES]]
   return %1 : !fir.logical<4>
 }
+
+func.func @bitcast_convert_fold_true() -> i1 {
+  %c3_i32 = arith.constant 3 : i32
+  %1 = fir.bitcast %c3_i32 : (i32) -> !fir.logical<4>
+  %2 = fir.convert %1 : (!fir.logical<4>) -> i1
+  return %2 : i1
+}
+// CHECK-LABEL:   func.func @bitcast_convert_fold_true() -> i1 {
+// CHECK:           %[[CONSTANT_0:.*]] = arith.constant true
+// CHECK:           return %[[CONSTANT_0]] : i1
+
+func.func @bitcast_convert_fold_false() -> i1 {
+  %c0_i32 = arith.constant 0 : i32
+  %1 = fir.bitcast %c0_i32 : (i32) -> !fir.logical<4>
+  %2 = fir.convert %1 : (!fir.logical<4>) -> i1
+  return %2 : i1
+}
+// CHECK-LABEL:   func.func @bitcast_convert_fold_false() -> i1 {
+// CHECK:           %[[CONSTANT_0:.*]] = arith.constant false
+// CHECK:           return %[[CONSTANT_0]] : i1
diff --git a/flang/test/Lower/constant-logical-transfer.f90 b/flang/test/Lower/constant-logical-transfer.f90
new file mode 100644
index 0000000000000..8f06de2086016
--- /dev/null
+++ b/flang/test/Lower/constant-logical-transfer.f90
@@ -0,0 +1,23 @@
+! Test lowering of non canonical LOGICAL constants.
+! RUN: %flang_fc1 -emit-hlfir %s -o - | FileCheck %s
+
+subroutine test_transfer_constant(l4, l8)
+  logical(4) :: l4
+  logical(8) :: l8
+  l4 = transfer(3, .true._4)
+  l8 = transfer(7, .true._8)
+end subroutine
+
+module constant_logical
+  logical(4) :: var(3) = [.true., transfer(3, .true._4), .false.]
+end module
+
+! CHECK-LABEL:  func.func @_QPtest_transfer_constant(
+! CHECK:          %[[CONSTANT_0:.*]] = arith.constant 3 : i32
+! CHECK:          %[[BITCAST_0:.*]] = fir.bitcast %[[CONSTANT_0]] : (i32) -> !fir.logical<4>
+! CHECK:          hlfir.assign %[[BITCAST_0]] to %{{.*}} : !fir.logical<4>, !fir.ref<!fir.logical<4>>
+! CHECK:          %[[CONSTANT_1:.*]] = arith.constant 7 : i64
+! CHECK:          %[[BITCAST_1:.*]] = fir.bitcast %[[CONSTANT_1]] : (i64) -> !fir.logical<8>
+! CHECK:          hlfir.assign %[[BITCAST_1]] to %{{.*}} : !fir.logical<8>, !fir.ref<!fir.logical<8>>
+
+! CHECK:        fir.global @_QMconstant_logicalEvar(dense<[1, 3, 0]> : tensor<3xi32>) : !fir.array<3x!fir.logical<4>>

``````````

</details>


https://github.com/llvm/llvm-project/pull/192417


More information about the flang-commits mailing list