Skip to content

[flang][clang] Add support for -finit-logical in Flang #150939

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 1 commit into
base: main
Choose a base branch
from

Conversation

NimishMishra
Copy link
Contributor

This PR adds the GNU gfortran compatible flag -finit-logical. It follows similar semantics as gfortran does

@llvmbot llvmbot added clang Clang issues not falling into any other category clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' flang:driver flang Flang issues not falling into any other category flang:fir-hlfir labels Jul 28, 2025
@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-clang-driver

Author: None (NimishMishra)

Changes

This PR adds the GNU gfortran compatible flag -finit-logical. It follows similar semantics as gfortran does


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

6 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+5-1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-2)
  • (modified) flang/include/flang/Lower/LoweringOptions.def (+6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+16)
  • (modified) flang/lib/Lower/Bridge.cpp (+73)
  • (added) flang/test/Lower/logical_init.f90 (+82)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e2ab046b26ae6..9b6ab62e13255 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6960,6 +6960,7 @@ def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;
 def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group>;
 
 // "f" options with values for gfortran.
+// Some of these options are visible for LLVM Flang too.
 def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
 def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
 def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
@@ -6967,7 +6968,10 @@ def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
 def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
 def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
 def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group<gfortran_Group>;
-def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
+def finit_logical_EQ : Joined<["-"], "finit-logical=">,
+                       Group<gfortran_Group>,
+                       Visibility<[FlangOption, FC1Option]>,
+                       HelpText<"Initialize logical type.">;
 def finit_real_EQ : Joined<["-"], "finit-real=">, Group<gfortran_Group>;
 def fmax_array_constructor_EQ : Joined<["-"], "fmax-array-constructor=">, Group<gfortran_Group>;
 def fmax_errors_EQ : Joined<["-"], "fmax-errors=">, Group<gfortran_Group>;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 7ab41e9b85a04..8478ce4ac57d5 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
        options::OPT_flang_deprecated_no_hlfir,
        options::OPT_fno_ppc_native_vec_elem_order,
        options::OPT_fppc_native_vec_elem_order, options::OPT_finit_global_zero,
-       options::OPT_fno_init_global_zero, options::OPT_frepack_arrays,
-       options::OPT_fno_repack_arrays,
+       options::OPT_finit_logical_EQ, options::OPT_fno_init_global_zero,
+       options::OPT_frepack_arrays, options::OPT_fno_repack_arrays,
        options::OPT_frepack_arrays_contiguity_EQ,
        options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
        options::OPT_ftime_report, options::OPT_ftime_report_EQ,
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 8135704971aa4..3e2bd9afe7ce8 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -74,5 +74,11 @@ ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
 /// If false, lower to the complex dialect of MLIR.
 /// On by default.
 ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
+
+/// Initialization for logical type
+/// 	-1  : No initialization
+///	 0  : Initialized to .FALSE.
+///	 1  : Initialized to .TRUE.
+ENUM_LOWERINGOPT(LogicalInit, signed, 2, -1)
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f55d866435997..97fae23f0e54b 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1541,6 +1541,22 @@ bool CompilerInvocation::createFromArgs(
   else
     invoc.loweringOpts.setInitGlobalZero(false);
 
+  // -finit-logical
+  if (const auto *arg =
+          args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) {
+    llvm::StringRef argValue = llvm::StringRef(arg->getValue());
+    if (argValue.lower() == "true")
+      invoc.loweringOpts.setLogicalInit(1);
+    else if (argValue.lower() == "false")
+      invoc.loweringOpts.setLogicalInit(0);
+    else {
+      const unsigned diagID = diags.getCustomDiagID(
+          clang::DiagnosticsEngine::Error,
+          "Invalid argument to -finit-logical. Must be <true/false>");
+      diags.Report(diagID);
+    }
+  }
+
   // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
   // -Rpass-analysis. This will be used later when processing and outputting the
   // remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 92aae792248c5..db66d9693b369 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5720,6 +5720,79 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   void instantiateVar(const Fortran::lower::pft::Variable &var,
                       Fortran::lower::AggregateStoreMap &storeMap) {
     Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap);
+
+    /// Implicit assignment is defined by the `-finit-*` family of flags.
+    /// These options do not initialize:
+    ///   1) Any variable already initialized
+    ///   2) objects with the POINTER attribute
+    ///   3) allocatable arrays
+    ///   4) variables that appear in an EQUIVALENCE statement
+
+    auto isEligibleForImplicitAssignment = [&var]() -> bool {
+      if (!var.hasSymbol())
+        return false;
+
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      if (const auto *details =
+              sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+        if (details->init())
+          return false;
+      }
+
+      if (sym.attrs().test(Fortran::semantics::Attr::POINTER))
+        return false;
+
+      if (sym.Rank() > 0 &&
+          sym.attrs().test(Fortran::semantics::Attr::ALLOCATABLE))
+        return false;
+
+      if (Fortran::lower::pft::getDependentVariableList(sym).size() > 1)
+        return false;
+
+      return true;
+    };
+
+    auto processImplicitAssignment = [&]() -> void {
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+      bool isInitLogicalFlagDefined =
+          (getLoweringOptions().getLogicalInit() == 1 ||
+           getLoweringOptions().getLogicalInit() == 0);
+
+      /*
+       * Process -finit-logical=true|false
+       * Create an implicit assignment of form `var = value`,
+       * where `value` is either true or false, and generically
+       * build the assignment.
+       */
+      if (isInitLogicalFlagDefined &&
+          declTy->category() ==
+              Fortran::semantics::DeclTypeSpec::Category::Logical) {
+        Fortran::parser::Expr expr =
+            Fortran::parser::Expr{Fortran::parser::LiteralConstant{
+                Fortran::parser::LogicalLiteralConstant{
+                    (getLoweringOptions().getLogicalInit() == 0) ? false : true,
+                    std::optional<Fortran::parser::KindParam>{}}}};
+        Fortran::parser::Designator designator = Fortran::parser::Designator{
+            Fortran::parser::DataRef{Fortran::parser::Name{
+                Fortran::parser::FindSourceLocation(sym.name()),
+                const_cast<Fortran::semantics::Symbol *>(&sym)}}};
+        designator.source = Fortran::parser::FindSourceLocation(sym.name());
+        Fortran::parser::Variable variable = Fortran::parser::Variable{
+            Fortran::common::Indirection<Fortran::parser::Designator>{
+                std::move(designator)}};
+        Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{
+            std::make_tuple(std::move(variable), std::move(expr))};
+        Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()};
+        const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt);
+        if (assign)
+          genAssignment(*assign);
+      }
+    };
+
+    if (isEligibleForImplicitAssignment())
+      processImplicitAssignment();
+
     if (var.hasSymbol())
       genOpenMPSymbolProperties(*this, var);
   }
diff --git a/flang/test/Lower/logical_init.f90 b/flang/test/Lower/logical_init.f90
new file mode 100644
index 0000000000000..4fd4506496e46
--- /dev/null
+++ b/flang/test/Lower/logical_init.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck --check-prefix=CHECK-UNINT %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=true -o - %s | FileCheck --check-prefix=CHECK-TRUE %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=false -o - %s | FileCheck --check-prefix=CHECK-FALSE %s
+
+subroutine logical_scalar
+!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+end subroutine
+
+
+subroutine logical_allocatable
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x
+end subroutine
+
+
+subroutine logical_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x(5)
+end subroutine
+
+
+subroutine logical_pointer
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, pointer :: x
+end subroutine
+
+
+subroutine logical_allocatable_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x(:)
+end subroutine
+
+
+subroutine logical_in_equivalence
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+  real :: y
+  equivalence(x,y)
+end subroutine

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

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

Author: None (NimishMishra)

Changes

This PR adds the GNU gfortran compatible flag -finit-logical. It follows similar semantics as gfortran does


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

6 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+5-1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-2)
  • (modified) flang/include/flang/Lower/LoweringOptions.def (+6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+16)
  • (modified) flang/lib/Lower/Bridge.cpp (+73)
  • (added) flang/test/Lower/logical_init.f90 (+82)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e2ab046b26ae6..9b6ab62e13255 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6960,6 +6960,7 @@ def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;
 def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group>;
 
 // "f" options with values for gfortran.
+// Some of these options are visible for LLVM Flang too.
 def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
 def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
 def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
@@ -6967,7 +6968,10 @@ def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
 def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
 def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
 def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group<gfortran_Group>;
-def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
+def finit_logical_EQ : Joined<["-"], "finit-logical=">,
+                       Group<gfortran_Group>,
+                       Visibility<[FlangOption, FC1Option]>,
+                       HelpText<"Initialize logical type.">;
 def finit_real_EQ : Joined<["-"], "finit-real=">, Group<gfortran_Group>;
 def fmax_array_constructor_EQ : Joined<["-"], "fmax-array-constructor=">, Group<gfortran_Group>;
 def fmax_errors_EQ : Joined<["-"], "fmax-errors=">, Group<gfortran_Group>;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 7ab41e9b85a04..8478ce4ac57d5 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
        options::OPT_flang_deprecated_no_hlfir,
        options::OPT_fno_ppc_native_vec_elem_order,
        options::OPT_fppc_native_vec_elem_order, options::OPT_finit_global_zero,
-       options::OPT_fno_init_global_zero, options::OPT_frepack_arrays,
-       options::OPT_fno_repack_arrays,
+       options::OPT_finit_logical_EQ, options::OPT_fno_init_global_zero,
+       options::OPT_frepack_arrays, options::OPT_fno_repack_arrays,
        options::OPT_frepack_arrays_contiguity_EQ,
        options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
        options::OPT_ftime_report, options::OPT_ftime_report_EQ,
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 8135704971aa4..3e2bd9afe7ce8 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -74,5 +74,11 @@ ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
 /// If false, lower to the complex dialect of MLIR.
 /// On by default.
 ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
+
+/// Initialization for logical type
+/// 	-1  : No initialization
+///	 0  : Initialized to .FALSE.
+///	 1  : Initialized to .TRUE.
+ENUM_LOWERINGOPT(LogicalInit, signed, 2, -1)
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f55d866435997..97fae23f0e54b 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1541,6 +1541,22 @@ bool CompilerInvocation::createFromArgs(
   else
     invoc.loweringOpts.setInitGlobalZero(false);
 
+  // -finit-logical
+  if (const auto *arg =
+          args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) {
+    llvm::StringRef argValue = llvm::StringRef(arg->getValue());
+    if (argValue.lower() == "true")
+      invoc.loweringOpts.setLogicalInit(1);
+    else if (argValue.lower() == "false")
+      invoc.loweringOpts.setLogicalInit(0);
+    else {
+      const unsigned diagID = diags.getCustomDiagID(
+          clang::DiagnosticsEngine::Error,
+          "Invalid argument to -finit-logical. Must be <true/false>");
+      diags.Report(diagID);
+    }
+  }
+
   // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
   // -Rpass-analysis. This will be used later when processing and outputting the
   // remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 92aae792248c5..db66d9693b369 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5720,6 +5720,79 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   void instantiateVar(const Fortran::lower::pft::Variable &var,
                       Fortran::lower::AggregateStoreMap &storeMap) {
     Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap);
+
+    /// Implicit assignment is defined by the `-finit-*` family of flags.
+    /// These options do not initialize:
+    ///   1) Any variable already initialized
+    ///   2) objects with the POINTER attribute
+    ///   3) allocatable arrays
+    ///   4) variables that appear in an EQUIVALENCE statement
+
+    auto isEligibleForImplicitAssignment = [&var]() -> bool {
+      if (!var.hasSymbol())
+        return false;
+
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      if (const auto *details =
+              sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+        if (details->init())
+          return false;
+      }
+
+      if (sym.attrs().test(Fortran::semantics::Attr::POINTER))
+        return false;
+
+      if (sym.Rank() > 0 &&
+          sym.attrs().test(Fortran::semantics::Attr::ALLOCATABLE))
+        return false;
+
+      if (Fortran::lower::pft::getDependentVariableList(sym).size() > 1)
+        return false;
+
+      return true;
+    };
+
+    auto processImplicitAssignment = [&]() -> void {
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+      bool isInitLogicalFlagDefined =
+          (getLoweringOptions().getLogicalInit() == 1 ||
+           getLoweringOptions().getLogicalInit() == 0);
+
+      /*
+       * Process -finit-logical=true|false
+       * Create an implicit assignment of form `var = value`,
+       * where `value` is either true or false, and generically
+       * build the assignment.
+       */
+      if (isInitLogicalFlagDefined &&
+          declTy->category() ==
+              Fortran::semantics::DeclTypeSpec::Category::Logical) {
+        Fortran::parser::Expr expr =
+            Fortran::parser::Expr{Fortran::parser::LiteralConstant{
+                Fortran::parser::LogicalLiteralConstant{
+                    (getLoweringOptions().getLogicalInit() == 0) ? false : true,
+                    std::optional<Fortran::parser::KindParam>{}}}};
+        Fortran::parser::Designator designator = Fortran::parser::Designator{
+            Fortran::parser::DataRef{Fortran::parser::Name{
+                Fortran::parser::FindSourceLocation(sym.name()),
+                const_cast<Fortran::semantics::Symbol *>(&sym)}}};
+        designator.source = Fortran::parser::FindSourceLocation(sym.name());
+        Fortran::parser::Variable variable = Fortran::parser::Variable{
+            Fortran::common::Indirection<Fortran::parser::Designator>{
+                std::move(designator)}};
+        Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{
+            std::make_tuple(std::move(variable), std::move(expr))};
+        Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()};
+        const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt);
+        if (assign)
+          genAssignment(*assign);
+      }
+    };
+
+    if (isEligibleForImplicitAssignment())
+      processImplicitAssignment();
+
     if (var.hasSymbol())
       genOpenMPSymbolProperties(*this, var);
   }
diff --git a/flang/test/Lower/logical_init.f90 b/flang/test/Lower/logical_init.f90
new file mode 100644
index 0000000000000..4fd4506496e46
--- /dev/null
+++ b/flang/test/Lower/logical_init.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck --check-prefix=CHECK-UNINT %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=true -o - %s | FileCheck --check-prefix=CHECK-TRUE %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=false -o - %s | FileCheck --check-prefix=CHECK-FALSE %s
+
+subroutine logical_scalar
+!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+end subroutine
+
+
+subroutine logical_allocatable
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x
+end subroutine
+
+
+subroutine logical_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x(5)
+end subroutine
+
+
+subroutine logical_pointer
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, pointer :: x
+end subroutine
+
+
+subroutine logical_allocatable_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x(:)
+end subroutine
+
+
+subroutine logical_in_equivalence
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+  real :: y
+  equivalence(x,y)
+end subroutine

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-clang

Author: None (NimishMishra)

Changes

This PR adds the GNU gfortran compatible flag -finit-logical. It follows similar semantics as gfortran does


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

6 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+5-1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-2)
  • (modified) flang/include/flang/Lower/LoweringOptions.def (+6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+16)
  • (modified) flang/lib/Lower/Bridge.cpp (+73)
  • (added) flang/test/Lower/logical_init.f90 (+82)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e2ab046b26ae6..9b6ab62e13255 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6960,6 +6960,7 @@ def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;
 def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group>;
 
 // "f" options with values for gfortran.
+// Some of these options are visible for LLVM Flang too.
 def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
 def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
 def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
@@ -6967,7 +6968,10 @@ def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
 def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
 def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
 def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group<gfortran_Group>;
-def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
+def finit_logical_EQ : Joined<["-"], "finit-logical=">,
+                       Group<gfortran_Group>,
+                       Visibility<[FlangOption, FC1Option]>,
+                       HelpText<"Initialize logical type.">;
 def finit_real_EQ : Joined<["-"], "finit-real=">, Group<gfortran_Group>;
 def fmax_array_constructor_EQ : Joined<["-"], "fmax-array-constructor=">, Group<gfortran_Group>;
 def fmax_errors_EQ : Joined<["-"], "fmax-errors=">, Group<gfortran_Group>;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 7ab41e9b85a04..8478ce4ac57d5 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
        options::OPT_flang_deprecated_no_hlfir,
        options::OPT_fno_ppc_native_vec_elem_order,
        options::OPT_fppc_native_vec_elem_order, options::OPT_finit_global_zero,
-       options::OPT_fno_init_global_zero, options::OPT_frepack_arrays,
-       options::OPT_fno_repack_arrays,
+       options::OPT_finit_logical_EQ, options::OPT_fno_init_global_zero,
+       options::OPT_frepack_arrays, options::OPT_fno_repack_arrays,
        options::OPT_frepack_arrays_contiguity_EQ,
        options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
        options::OPT_ftime_report, options::OPT_ftime_report_EQ,
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 8135704971aa4..3e2bd9afe7ce8 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -74,5 +74,11 @@ ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
 /// If false, lower to the complex dialect of MLIR.
 /// On by default.
 ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
+
+/// Initialization for logical type
+/// 	-1  : No initialization
+///	 0  : Initialized to .FALSE.
+///	 1  : Initialized to .TRUE.
+ENUM_LOWERINGOPT(LogicalInit, signed, 2, -1)
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f55d866435997..97fae23f0e54b 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1541,6 +1541,22 @@ bool CompilerInvocation::createFromArgs(
   else
     invoc.loweringOpts.setInitGlobalZero(false);
 
+  // -finit-logical
+  if (const auto *arg =
+          args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) {
+    llvm::StringRef argValue = llvm::StringRef(arg->getValue());
+    if (argValue.lower() == "true")
+      invoc.loweringOpts.setLogicalInit(1);
+    else if (argValue.lower() == "false")
+      invoc.loweringOpts.setLogicalInit(0);
+    else {
+      const unsigned diagID = diags.getCustomDiagID(
+          clang::DiagnosticsEngine::Error,
+          "Invalid argument to -finit-logical. Must be <true/false>");
+      diags.Report(diagID);
+    }
+  }
+
   // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
   // -Rpass-analysis. This will be used later when processing and outputting the
   // remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 92aae792248c5..db66d9693b369 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5720,6 +5720,79 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   void instantiateVar(const Fortran::lower::pft::Variable &var,
                       Fortran::lower::AggregateStoreMap &storeMap) {
     Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap);
+
+    /// Implicit assignment is defined by the `-finit-*` family of flags.
+    /// These options do not initialize:
+    ///   1) Any variable already initialized
+    ///   2) objects with the POINTER attribute
+    ///   3) allocatable arrays
+    ///   4) variables that appear in an EQUIVALENCE statement
+
+    auto isEligibleForImplicitAssignment = [&var]() -> bool {
+      if (!var.hasSymbol())
+        return false;
+
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      if (const auto *details =
+              sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+        if (details->init())
+          return false;
+      }
+
+      if (sym.attrs().test(Fortran::semantics::Attr::POINTER))
+        return false;
+
+      if (sym.Rank() > 0 &&
+          sym.attrs().test(Fortran::semantics::Attr::ALLOCATABLE))
+        return false;
+
+      if (Fortran::lower::pft::getDependentVariableList(sym).size() > 1)
+        return false;
+
+      return true;
+    };
+
+    auto processImplicitAssignment = [&]() -> void {
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+      bool isInitLogicalFlagDefined =
+          (getLoweringOptions().getLogicalInit() == 1 ||
+           getLoweringOptions().getLogicalInit() == 0);
+
+      /*
+       * Process -finit-logical=true|false
+       * Create an implicit assignment of form `var = value`,
+       * where `value` is either true or false, and generically
+       * build the assignment.
+       */
+      if (isInitLogicalFlagDefined &&
+          declTy->category() ==
+              Fortran::semantics::DeclTypeSpec::Category::Logical) {
+        Fortran::parser::Expr expr =
+            Fortran::parser::Expr{Fortran::parser::LiteralConstant{
+                Fortran::parser::LogicalLiteralConstant{
+                    (getLoweringOptions().getLogicalInit() == 0) ? false : true,
+                    std::optional<Fortran::parser::KindParam>{}}}};
+        Fortran::parser::Designator designator = Fortran::parser::Designator{
+            Fortran::parser::DataRef{Fortran::parser::Name{
+                Fortran::parser::FindSourceLocation(sym.name()),
+                const_cast<Fortran::semantics::Symbol *>(&sym)}}};
+        designator.source = Fortran::parser::FindSourceLocation(sym.name());
+        Fortran::parser::Variable variable = Fortran::parser::Variable{
+            Fortran::common::Indirection<Fortran::parser::Designator>{
+                std::move(designator)}};
+        Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{
+            std::make_tuple(std::move(variable), std::move(expr))};
+        Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()};
+        const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt);
+        if (assign)
+          genAssignment(*assign);
+      }
+    };
+
+    if (isEligibleForImplicitAssignment())
+      processImplicitAssignment();
+
     if (var.hasSymbol())
       genOpenMPSymbolProperties(*this, var);
   }
diff --git a/flang/test/Lower/logical_init.f90 b/flang/test/Lower/logical_init.f90
new file mode 100644
index 0000000000000..4fd4506496e46
--- /dev/null
+++ b/flang/test/Lower/logical_init.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck --check-prefix=CHECK-UNINT %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=true -o - %s | FileCheck --check-prefix=CHECK-TRUE %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=false -o - %s | FileCheck --check-prefix=CHECK-FALSE %s
+
+subroutine logical_scalar
+!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+end subroutine
+
+
+subroutine logical_allocatable
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x
+end subroutine
+
+
+subroutine logical_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x(5)
+end subroutine
+
+
+subroutine logical_pointer
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, pointer :: x
+end subroutine
+
+
+subroutine logical_allocatable_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x(:)
+end subroutine
+
+
+subroutine logical_in_equivalence
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+  real :: y
+  equivalence(x,y)
+end subroutine

@llvmbot
Copy link
Member

llvmbot commented Jul 28, 2025

@llvm/pr-subscribers-flang-driver

Author: None (NimishMishra)

Changes

This PR adds the GNU gfortran compatible flag -finit-logical. It follows similar semantics as gfortran does


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

6 Files Affected:

  • (modified) clang/include/clang/Driver/Options.td (+5-1)
  • (modified) clang/lib/Driver/ToolChains/Flang.cpp (+2-2)
  • (modified) flang/include/flang/Lower/LoweringOptions.def (+6)
  • (modified) flang/lib/Frontend/CompilerInvocation.cpp (+16)
  • (modified) flang/lib/Lower/Bridge.cpp (+73)
  • (added) flang/test/Lower/logical_init.f90 (+82)
diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td
index e2ab046b26ae6..9b6ab62e13255 100644
--- a/clang/include/clang/Driver/Options.td
+++ b/clang/include/clang/Driver/Options.td
@@ -6960,6 +6960,7 @@ def A_DASH : Joined<["-"], "A-">, Group<gfortran_Group>;
 def static_libgfortran : Flag<["-"], "static-libgfortran">, Group<gfortran_Group>;
 
 // "f" options with values for gfortran.
+// Some of these options are visible for LLVM Flang too.
 def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
 def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
 def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
@@ -6967,7 +6968,10 @@ def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
 def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
 def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
 def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group<gfortran_Group>;
-def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
+def finit_logical_EQ : Joined<["-"], "finit-logical=">,
+                       Group<gfortran_Group>,
+                       Visibility<[FlangOption, FC1Option]>,
+                       HelpText<"Initialize logical type.">;
 def finit_real_EQ : Joined<["-"], "finit-real=">, Group<gfortran_Group>;
 def fmax_array_constructor_EQ : Joined<["-"], "fmax-array-constructor=">, Group<gfortran_Group>;
 def fmax_errors_EQ : Joined<["-"], "fmax-errors=">, Group<gfortran_Group>;
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index 7ab41e9b85a04..8478ce4ac57d5 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
        options::OPT_flang_deprecated_no_hlfir,
        options::OPT_fno_ppc_native_vec_elem_order,
        options::OPT_fppc_native_vec_elem_order, options::OPT_finit_global_zero,
-       options::OPT_fno_init_global_zero, options::OPT_frepack_arrays,
-       options::OPT_fno_repack_arrays,
+       options::OPT_finit_logical_EQ, options::OPT_fno_init_global_zero,
+       options::OPT_frepack_arrays, options::OPT_fno_repack_arrays,
        options::OPT_frepack_arrays_contiguity_EQ,
        options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
        options::OPT_ftime_report, options::OPT_ftime_report_EQ,
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 8135704971aa4..3e2bd9afe7ce8 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -74,5 +74,11 @@ ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
 /// If false, lower to the complex dialect of MLIR.
 /// On by default.
 ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
+
+/// Initialization for logical type
+/// 	-1  : No initialization
+///	 0  : Initialized to .FALSE.
+///	 1  : Initialized to .TRUE.
+ENUM_LOWERINGOPT(LogicalInit, signed, 2, -1)
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index f55d866435997..97fae23f0e54b 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1541,6 +1541,22 @@ bool CompilerInvocation::createFromArgs(
   else
     invoc.loweringOpts.setInitGlobalZero(false);
 
+  // -finit-logical
+  if (const auto *arg =
+          args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) {
+    llvm::StringRef argValue = llvm::StringRef(arg->getValue());
+    if (argValue.lower() == "true")
+      invoc.loweringOpts.setLogicalInit(1);
+    else if (argValue.lower() == "false")
+      invoc.loweringOpts.setLogicalInit(0);
+    else {
+      const unsigned diagID = diags.getCustomDiagID(
+          clang::DiagnosticsEngine::Error,
+          "Invalid argument to -finit-logical. Must be <true/false>");
+      diags.Report(diagID);
+    }
+  }
+
   // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or
   // -Rpass-analysis. This will be used later when processing and outputting the
   // remarks generated by LLVM in ExecuteCompilerInvocation.cpp.
diff --git a/flang/lib/Lower/Bridge.cpp b/flang/lib/Lower/Bridge.cpp
index 92aae792248c5..db66d9693b369 100644
--- a/flang/lib/Lower/Bridge.cpp
+++ b/flang/lib/Lower/Bridge.cpp
@@ -5720,6 +5720,79 @@ class FirConverter : public Fortran::lower::AbstractConverter {
   void instantiateVar(const Fortran::lower::pft::Variable &var,
                       Fortran::lower::AggregateStoreMap &storeMap) {
     Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap);
+
+    /// Implicit assignment is defined by the `-finit-*` family of flags.
+    /// These options do not initialize:
+    ///   1) Any variable already initialized
+    ///   2) objects with the POINTER attribute
+    ///   3) allocatable arrays
+    ///   4) variables that appear in an EQUIVALENCE statement
+
+    auto isEligibleForImplicitAssignment = [&var]() -> bool {
+      if (!var.hasSymbol())
+        return false;
+
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      if (const auto *details =
+              sym.detailsIf<Fortran::semantics::ObjectEntityDetails>()) {
+        if (details->init())
+          return false;
+      }
+
+      if (sym.attrs().test(Fortran::semantics::Attr::POINTER))
+        return false;
+
+      if (sym.Rank() > 0 &&
+          sym.attrs().test(Fortran::semantics::Attr::ALLOCATABLE))
+        return false;
+
+      if (Fortran::lower::pft::getDependentVariableList(sym).size() > 1)
+        return false;
+
+      return true;
+    };
+
+    auto processImplicitAssignment = [&]() -> void {
+      const Fortran::semantics::Symbol &sym = var.getSymbol();
+      const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType();
+      bool isInitLogicalFlagDefined =
+          (getLoweringOptions().getLogicalInit() == 1 ||
+           getLoweringOptions().getLogicalInit() == 0);
+
+      /*
+       * Process -finit-logical=true|false
+       * Create an implicit assignment of form `var = value`,
+       * where `value` is either true or false, and generically
+       * build the assignment.
+       */
+      if (isInitLogicalFlagDefined &&
+          declTy->category() ==
+              Fortran::semantics::DeclTypeSpec::Category::Logical) {
+        Fortran::parser::Expr expr =
+            Fortran::parser::Expr{Fortran::parser::LiteralConstant{
+                Fortran::parser::LogicalLiteralConstant{
+                    (getLoweringOptions().getLogicalInit() == 0) ? false : true,
+                    std::optional<Fortran::parser::KindParam>{}}}};
+        Fortran::parser::Designator designator = Fortran::parser::Designator{
+            Fortran::parser::DataRef{Fortran::parser::Name{
+                Fortran::parser::FindSourceLocation(sym.name()),
+                const_cast<Fortran::semantics::Symbol *>(&sym)}}};
+        designator.source = Fortran::parser::FindSourceLocation(sym.name());
+        Fortran::parser::Variable variable = Fortran::parser::Variable{
+            Fortran::common::Indirection<Fortran::parser::Designator>{
+                std::move(designator)}};
+        Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{
+            std::make_tuple(std::move(variable), std::move(expr))};
+        Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()};
+        const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt);
+        if (assign)
+          genAssignment(*assign);
+      }
+    };
+
+    if (isEligibleForImplicitAssignment())
+      processImplicitAssignment();
+
     if (var.hasSymbol())
       genOpenMPSymbolProperties(*this, var);
   }
diff --git a/flang/test/Lower/logical_init.f90 b/flang/test/Lower/logical_init.f90
new file mode 100644
index 0000000000000..4fd4506496e46
--- /dev/null
+++ b/flang/test/Lower/logical_init.f90
@@ -0,0 +1,82 @@
+! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck --check-prefix=CHECK-UNINT %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=true -o - %s | FileCheck --check-prefix=CHECK-TRUE %s
+! RUN: %flang_fc1 -emit-fir -finit-logical=false -o - %s | FileCheck --check-prefix=CHECK-FALSE %s
+
+subroutine logical_scalar
+!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+end subroutine
+
+
+subroutine logical_allocatable
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x
+end subroutine
+
+
+subroutine logical_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x(5)
+end subroutine
+
+
+subroutine logical_pointer
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, pointer :: x
+end subroutine
+
+
+subroutine logical_allocatable_array
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical, allocatable :: x(:)
+end subroutine
+
+
+subroutine logical_in_equivalence
+!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4>
+
+!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+
+!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
+  logical :: x
+  real :: y
+  equivalence(x,y)
+end subroutine

Copy link
Contributor

@kiranchandramohan kiranchandramohan left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A few trivial comments.

def finit_logical_EQ : Joined<["-"], "finit-logical=">,
Group<gfortran_Group>,
Visibility<[FlangOption, FC1Option]>,
HelpText<"Initialize logical type.">;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: list the possible values here.

def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group<gfortran_Group>;
def fcheck_EQ : Joined<["-"], "fcheck=">, Group<gfortran_Group>;
def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group<gfortran_Group>;
def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group<gfortran_Group>;
def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group<gfortran_Group>;
def finit_character_EQ : Joined<["-"], "finit-character=">, Group<gfortran_Group>;
def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group<gfortran_Group>;
def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
def finit_logical_EQ : Joined<["-"], "finit-logical=">,
Group<gfortran_Group>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should probably be moved from the gfortran group.

Comment on lines +1548 to +1551
if (argValue.lower() == "true")
invoc.loweringOpts.setLogicalInit(1);
else if (argValue.lower() == "false")
invoc.loweringOpts.setLogicalInit(0);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: braces to match the else.

def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group<gfortran_Group>;
def finit_logical_EQ : Joined<["-"], "finit-logical=">,
Group<gfortran_Group>,
Visibility<[FlangOption, FC1Option]>,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Need a driver test as well.

Comment on lines +6 to +13
!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>

!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>

!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4>
!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is probably better to test that the variable is assigned the relevant value.

logical :: x
real :: y
equivalence(x,y)
end subroutine
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: Add a check that derived components are not affected.

Comment on lines +5726 to +5729
/// 1) Any variable already initialized
/// 2) objects with the POINTER attribute
/// 3) allocatable arrays
/// 4) variables that appear in an EQUIVALENCE statement
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this to match the implementation in gfortran?

@@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
options::OPT_flang_deprecated_no_hlfir,
options::OPT_fno_ppc_native_vec_elem_order,
options::OPT_fppc_native_vec_elem_order, options::OPT_finit_global_zero,
options::OPT_fno_init_global_zero, options::OPT_frepack_arrays,
options::OPT_fno_repack_arrays,
options::OPT_finit_logical_EQ, options::OPT_fno_init_global_zero,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: It may be better to add OPT_finit_logical_EQ before OPT_finit_global_zero. This way, we don't split OPT_finit_global_zero and OPT_fno_init_global_zero since the OPT_<name> and OPT_no_<name> values are typically listed next to one another here.


/// Initialization for logical type
/// -1 : No initialization
/// 0 : Initialized to .FALSE.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

NIT: Perhaps align the colons.

if (const auto *arg =
args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) {
llvm::StringRef argValue = llvm::StringRef(arg->getValue());
if (argValue.lower() == "true")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider using StringRef::compare_insensitive here. lower() creates a new std::string which seems unnecessary.

else if (argValue.lower() == "false")
invoc.loweringOpts.setLogicalInit(0);
else {
const unsigned diagID = diags.getCustomDiagID(
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we use an existing clang driver diagnostic here instead of creating custom id? For instance

 diags.Report(clang::diag::err_drv_invalid_value_with_suggestion)
          << arg->getAsString(args) << arg->getValue() << "false true";

Comment on lines +5768 to +5790
if (isInitLogicalFlagDefined &&
declTy->category() ==
Fortran::semantics::DeclTypeSpec::Category::Logical) {
Fortran::parser::Expr expr =
Fortran::parser::Expr{Fortran::parser::LiteralConstant{
Fortran::parser::LogicalLiteralConstant{
(getLoweringOptions().getLogicalInit() == 0) ? false : true,
std::optional<Fortran::parser::KindParam>{}}}};
Fortran::parser::Designator designator = Fortran::parser::Designator{
Fortran::parser::DataRef{Fortran::parser::Name{
Fortran::parser::FindSourceLocation(sym.name()),
const_cast<Fortran::semantics::Symbol *>(&sym)}}};
designator.source = Fortran::parser::FindSourceLocation(sym.name());
Fortran::parser::Variable variable = Fortran::parser::Variable{
Fortran::common::Indirection<Fortran::parser::Designator>{
std::move(designator)}};
Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{
std::make_tuple(std::move(variable), std::move(expr))};
Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()};
const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt);
if (assign)
genAssignment(*assign);
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No action required. But have you considered just generating an hlfir.assign?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
clang:driver 'clang' and 'clang++' user-facing binaries. Not 'clang-cl' clang Clang issues not falling into any other category flang:driver flang:fir-hlfir flang Flang issues not falling into any other category
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants