[clang] [flang] [flang] Add -finit-local= to initialize automatic variables (PR #216164)

Daniel Chen via cfe-commits cfe-commits at lists.llvm.org
Sat Aug 15 08:08:54 PDT 2026


https://github.com/DanielCChen updated https://github.com/llvm/llvm-project/pull/216164

>From 940ae6b6b2379a76c4bbddf3c8a94cd7a58ee34e Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Thu, 13 Aug 2026 15:30:12 -0400
Subject: [PATCH 1/4] [flang] Add -finit-local= to initialize automatic
 variables

---
 clang/include/clang/Options/FlangOptions.td   |  18 +-
 clang/lib/Driver/ToolChains/Flang.cpp         |   4 +-
 flang/docs/ReleaseNotes.md                    |   5 +
 flang/include/flang/Lower/LoweringOptions.def |   6 +
 flang/include/flang/Lower/LoweringOptions.h   |  22 +
 flang/lib/Frontend/CompilerInvocation.cpp     |  27 +
 flang/lib/Lower/ConvertVariable.cpp           | 236 +++++++++
 flang/test/Driver/finit-local.f90             |  33 ++
 flang/test/Lower/finit-local-f128.f90         |  64 +++
 flang/test/Lower/finit-local.f90              | 496 ++++++++++++++++++
 flang/tools/bbc/bbc.cpp                       |  39 ++
 11 files changed, 947 insertions(+), 3 deletions(-)
 create mode 100644 flang/test/Driver/finit-local.f90
 create mode 100644 flang/test/Lower/finit-local-f128.f90
 create mode 100644 flang/test/Lower/finit-local.f90

diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index cf40d0b909d8f..d3402467b6f8e 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -58,7 +58,6 @@ defm dump_parse_tree : BooleanFFlag<"dump-parse-tree">, Group<gfortran_Group>;
 defm external_blas : BooleanFFlag<"external-blas">, Group<gfortran_Group>;
 defm f2c : BooleanFFlag<"f2c">, Group<gfortran_Group>;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, Group<gfortran_Group>;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group<gfortran_Group>;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, Group<gfortran_Group>;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, Group<gfortran_Group>;
 defm module_private : BooleanFFlag<"module-private">, Group<gfortran_Group>;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", "init-global-zero",
   PosFlag<SetTrue, [], [], "Zero initialize globals without default initialization (default)">,
   NegFlag<SetFalse, [], [], "Do not zero initialize globals without default initialization">>;
 
+// -finit-local=<zero|nan|snan|0x<hex>>
+// Initialize automatic (local, stack) variables that have no explicit or
+// default initialization.  -finit-local-zero is a GFortran compatibility alias
+// for -finit-local=zero.
+def finit_local_EQ : Joined<["-"], "finit-local=">,
+  Group<f_Group>,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default initialization. "
+           "Accepts: zero, nan, snan, or 0x<hex-byte>.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group<f_Group>,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default initialization "
+           "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias<finit_local_EQ>, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, Group<f_Group>,
   HelpText<"An allocatable left-hand side of an intrinsic assignment is assumed to be allocated and match the shape/type of the right-hand side">;
 def frealloc_lhs : Flag<["-"], "frealloc-lhs">, Group<f_Group>,
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp
index a48e41159f367..096c592beb7ad 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -359,8 +359,8 @@ void Flang::addCodegenOptions(const ArgList &Args,
       {options::OPT_fdo_concurrent_to_openmp_EQ,
        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_fno_init_global_zero, options::OPT_finit_local_EQ,
+       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/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index bbc7084c4a757..660166e8099d0 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -57,6 +57,11 @@ page](https://llvm.org/releases/).
 - Added `-gz` and `-gz=<format>` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=<val>` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x<hex-byte>` (e.g. `0xAA`). The gfortran
+  compatibility alias `-finit-local-zero` is equivalent to `-finit-local=zero`.
+
 ## Windows Support
 
 ## Fortran Language Changes in Flang
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 61ccb2ac19bdd..43c48503f382d 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -97,5 +97,11 @@ ENUM_LOWERINGOPT(FPMaxminBehavior, Fortran::common::FPMaxminBehavior, 2, 0)
 /// 0 means no trapping. Bit values match IEEE_FLAG_TYPE encoding.
 ENUM_LOWERINGOPT(FPExceptionTraps, unsigned, 8, 0)
 
+/// Initialization mode for automatic variables that have no explicit or
+/// default initialization (-finit-local= / -finit-local-zero).
+/// Off by default.
+ENUM_LOWERINGOPT(InitLocalMode, Fortran::lower::InitLocalKind, 3,
+                 Fortran::lower::InitLocalKind::Off)
+
 #undef LOWERINGOPT
 #undef ENUM_LOWERINGOPT
diff --git a/flang/include/flang/Lower/LoweringOptions.h b/flang/include/flang/Lower/LoweringOptions.h
index d44d5f73eeb67..7f24c02c57c79 100644
--- a/flang/include/flang/Lower/LoweringOptions.h
+++ b/flang/include/flang/Lower/LoweringOptions.h
@@ -17,9 +17,20 @@
 
 #include "flang/Support/FPMaxminBehavior.h"
 #include "flang/Support/MathOptionsBase.h"
+#include <cstdint>
 
 namespace Fortran::lower {
 
+/// Initialization mode for automatic (local) variables without explicit
+/// or default initialization, selected via -finit-local=.
+enum class InitLocalKind {
+  Off,  ///< No initialization (default)
+  Zero, ///< Fill with 0x00 bytes
+  Hex,  ///< Fill with a user-supplied byte pattern
+  QNaN, ///< Quiet NaN for FP; 0xAA byte-splat for non-FP types
+  SNaN, ///< Signalling NaN for FP; 0xAA byte-splat for non-FP types
+};
+
 class LoweringOptionsBase {
 public:
 #define LOWERINGOPT(Name, Bits, Default) unsigned Name : Bits;
@@ -52,7 +63,18 @@ class LoweringOptions : public LoweringOptionsBase {
 
   Fortran::common::MathOptionsBase &getMathOptions() { return MathOptions; }
 
+  /// Returns the byte pattern used for -finit-local=0x<hex>.
+  uint8_t getInitLocalPattern() const { return InitLocalPattern; }
+  LoweringOptions &setInitLocalPattern(uint8_t V) {
+    InitLocalPattern = V;
+    return *this;
+  }
+
 private:
+  /// Byte pattern for -finit-local=0x<hex>. Only meaningful when
+  /// getInitLocalMode() == InitLocalKind::Hex.
+  uint8_t InitLocalPattern = 0;
+
   /// Options for handling/optimizing mathematical computations.
   Fortran::common::MathOptionsBase MathOptions;
 };
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp
index b57bc4583be38..5380a51a16345 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1755,6 +1755,33 @@ bool CompilerInvocation::createFromArgs(
   else
     invoc.loweringOpts.setInitGlobalZero(false);
 
+  // -finit-local=<zero|nan|snan|0x<hex>>  and  -finit-local-zero
+  // (-finit-local-zero is an alias that the driver already expands to
+  //  -finit-local=zero, so we only need to handle OPT_finit_local_EQ here.)
+  if (const llvm::opt::Arg *a =
+          args.getLastArg(clang::options::OPT_finit_local_EQ)) {
+    llvm::StringRef val = a->getValue();
+    if (val == "zero") {
+      invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
+    } else if (val == "nan") {
+      invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::QNaN);
+    } else if (val == "snan") {
+      invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::SNaN);
+    } else if (val.starts_with("0x") || val.starts_with("0X")) {
+      unsigned long long hexVal = 0;
+      if (val.drop_front(2).getAsInteger(16, hexVal) || hexVal > 0xFF) {
+        diags.Report(clang::diag::err_drv_invalid_value)
+            << a->getAsString(args) << val;
+      } else {
+        invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Hex);
+        invoc.loweringOpts.setInitLocalPattern(static_cast<uint8_t>(hexVal));
+      }
+    } else {
+      diags.Report(clang::diag::err_drv_invalid_value)
+          << a->getAsString(args) << val;
+    }
+  }
+
   // 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/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index a808905850922..03ebe82cb52ce 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -19,6 +19,7 @@
 #include "flang/Lower/ConvertConstant.h"
 #include "flang/Lower/ConvertExprToHLFIR.h"
 #include "flang/Lower/ConvertProcedureDesignator.h"
+#include "flang/Lower/LoweringOptions.h"
 #include "flang/Lower/Mangler.h"
 #include "flang/Lower/MultiImageFortran.h"
 #include "flang/Lower/OpenACC.h"
@@ -45,7 +46,10 @@
 #include "flang/Runtime/allocator-registry-consts.h"
 #include "flang/Semantics/tools.h"
 #include "flang/Semantics/type.h"
+#include "mlir/Dialect/Complex/IR/Complex.h"
 #include "mlir/Dialect/OpenACC/OpenACC.h"
+#include "llvm/ADT/APFloat.h"
+#include "llvm/ADT/APInt.h"
 #include "llvm/ADT/SmallVector.h"
 #include "llvm/Support/CommandLine.h"
 #include "llvm/Support/Debug.h"
@@ -1250,6 +1254,236 @@ getSafeRepackAttrs(Fortran::lower::AbstractConverter &converter) {
   return attrs.empty() ? mlir::ArrayAttr{} : builder.getArrayAttr(attrs);
 }
 
+//===----------------------------------------------------------------------===//
+// -finit-local= helpers
+//===----------------------------------------------------------------------===//
+
+/// Returns true when \p var is an automatic local variable eligible for
+/// -finit-local= initialization. Excluded: variables without a symbol,
+/// globals, dummy arguments, SAVE'd vars, ALLOCATABLE/POINTER, vars in
+/// an EQUIVALENCE set, and vars with explicit or default initialization.
+static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) {
+  if (!var.hasSymbol() || var.isGlobal())
+    return false;
+  const Fortran::semantics::Symbol &sym = var.getSymbol();
+  if (Fortran::semantics::IsDummy(sym))
+    return false;
+  if (Fortran::semantics::IsSaved(sym))
+    return false;
+  if (Fortran::semantics::IsAllocatableOrPointer(sym))
+    return false;
+  if (Fortran::lower::hasDefaultInitialization(sym))
+    return false;
+  if (const auto *obj =
+          sym.detailsIf<Fortran::semantics::ObjectEntityDetails>())
+    if (obj->init())
+      return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+    return false;
+  return true;
+}
+
+/// Build a constant whose every byte equals \p bytePat.
+/// FP types: bitcast from an integer splat. Complex: apply to both parts.
+/// Character: falls back to fir.zero_bits (see TODO). Derived types are
+/// handled by the caller before this function is reached.
+static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
+                                    mlir::Location loc, mlir::Type ty,
+                                    uint8_t bytePat) {
+  mlir::Type eleTy = fir::unwrapSequenceType(ty);
+
+  // Build an integer constant of the given bit width from a byte splat.
+  auto makeIntCst = [&](unsigned bits) -> mlir::Value {
+    llvm::APInt byteVal(8, bytePat);
+    llvm::APInt splat = llvm::APInt::getSplat(bits, byteVal);
+    mlir::Type intTy = builder.getIntegerType(bits);
+    return mlir::arith::ConstantOp::create(
+        builder, loc, intTy, builder.getIntegerAttr(intTy, splat));
+  };
+
+  if (auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy)) {
+    unsigned bits = fpTy.getWidth();
+    mlir::Value intCst = makeIntCst(bits);
+    return mlir::arith::BitcastOp::create(builder, loc, fpTy, intCst);
+  }
+  if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(eleTy)) {
+    return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy)) {
+    mlir::Type partTy = cplxTy.getElementType();
+    mlir::Value partVal = genByteSplatInit(builder, loc, partTy, bytePat);
+    return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal,
+                                           partVal);
+  }
+  // TODO: CHARACTER falls back to zero; a future improvement should fill each
+  // storage unit with the byte pattern.
+  return fir::ZeroOp::create(builder, loc, eleTy);
+}
+
+/// Build a quiet or signalling NaN constant of the given FP type.
+/// The payload is all-ones (matching clang's initializationPatternFor() and
+/// the RFC spec), and the sign bit is set (negative NaN).
+static mlir::Value genFPNaNInit(fir::FirOpBuilder &builder, mlir::Location loc,
+                                mlir::FloatType fpTy, bool isSignalling) {
+  const llvm::fltSemantics &sem = fpTy.getFloatSemantics();
+  // All-ones payload (precision-1 mantissa bits), negative sign, per RFC.
+  llvm::APInt payload = llvm::APInt::getAllOnes(sem.precision - 1);
+  llvm::APFloat apf =
+      isSignalling ? llvm::APFloat::getSNaN(sem, /*Negative=*/true, &payload)
+                   : llvm::APFloat::getQNaN(sem, /*Negative=*/true, &payload);
+  return mlir::arith::ConstantFloatOp::create(builder, loc, fpTy, apf);
+}
+
+/// Emit a store of the -finit-local= pattern for a single scalar address.
+/// Complex types get NaN on both parts; other non-FP types use 0xAA byte-splat
+/// for nan/snan modes.
+static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc,
+                              mlir::Type ty, mlir::Value addr,
+                              Fortran::lower::InitLocalKind mode,
+                              uint8_t hexByte) {
+  mlir::Value val;
+  auto fpTy = mlir::dyn_cast<mlir::FloatType>(ty);
+  auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(ty);
+  switch (mode) {
+  case Fortran::lower::InitLocalKind::Zero:
+    val = fir::ZeroOp::create(builder, loc, ty);
+    break;
+  case Fortran::lower::InitLocalKind::Hex:
+    val = genByteSplatInit(builder, loc, ty, hexByte);
+    break;
+  case Fortran::lower::InitLocalKind::QNaN:
+    if (fpTy) {
+      val = genFPNaNInit(builder, loc, fpTy, /*signalling=*/false);
+    } else if (cplxTy) {
+      auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+      mlir::Value nanPart =
+          genFPNaNInit(builder, loc, partFpTy, /*signalling=*/false);
+      val = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+                                            nanPart);
+    } else {
+      val = genByteSplatInit(builder, loc, ty, 0xAA);
+    }
+    break;
+  case Fortran::lower::InitLocalKind::SNaN:
+    if (fpTy) {
+      val = genFPNaNInit(builder, loc, fpTy, /*signalling=*/true);
+    } else if (cplxTy) {
+      auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+      mlir::Value nanPart =
+          genFPNaNInit(builder, loc, partFpTy, /*signalling=*/true);
+      val = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+                                            nanPart);
+    } else {
+      val = genByteSplatInit(builder, loc, ty, 0xAA);
+    }
+    break;
+  default:
+    llvm_unreachable("unexpected InitLocalKind in genInitLocalStore");
+  }
+  fir::StoreOp::create(builder, loc, val, addr);
+}
+
+/// Initialize all storage of the local variable \p var per -finit-local= mode.
+/// Arrays use insert_on_range. Derived types walk fields for nan/snan/hex.
+/// Scalars store directly.
+static void genInitLocal(Fortran::lower::AbstractConverter &converter,
+                         const Fortran::lower::pft::Variable &var,
+                         Fortran::lower::SymMap &symMap) {
+  Fortran::lower::InitLocalKind mode =
+      converter.getLoweringOptions().getInitLocalMode();
+  if (mode == Fortran::lower::InitLocalKind::Off)
+    return;
+  if (!shouldInitLocal(var))
+    return;
+
+  fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+  mlir::Location loc = converter.getCurrentLocation();
+  uint8_t hexByte = converter.getLoweringOptions().getInitLocalPattern();
+
+  fir::ExtendedValue exv =
+      converter.getSymbolExtendedValue(var.getSymbol(), &symMap);
+  mlir::Value base = fir::getBase(exv);
+  mlir::Type storeTy = fir::unwrapRefType(base.getType());
+
+  if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(storeTy)) {
+    // Array: build element constant and use insert_on_range.
+    mlir::Type eleTy = seqTy.getEleTy();
+    auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy);
+    auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy);
+    mlir::Value elePat;
+    switch (mode) {
+    case Fortran::lower::InitLocalKind::Zero:
+      elePat = fir::ZeroOp::create(builder, loc, eleTy);
+      break;
+    case Fortran::lower::InitLocalKind::Hex:
+      elePat = genByteSplatInit(builder, loc, eleTy, hexByte);
+      break;
+    case Fortran::lower::InitLocalKind::QNaN:
+      if (fpTy)
+        elePat = genFPNaNInit(builder, loc, fpTy, false);
+      else if (cplxTy) {
+        auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+        mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, false);
+        elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+                                                 nanPart);
+      } else
+        elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+      break;
+    case Fortran::lower::InitLocalKind::SNaN:
+      if (fpTy)
+        elePat = genFPNaNInit(builder, loc, fpTy, true);
+      else if (cplxTy) {
+        auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+        mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, true);
+        elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+                                                 nanPart);
+      } else
+        elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+      break;
+    default:
+      llvm_unreachable("unexpected InitLocalKind");
+    }
+    // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector.
+    llvm::SmallVector<int64_t> rangeBounds;
+    bool hasUnknown = false;
+    for (auto dim : seqTy.getShape()) {
+      if (dim == fir::SequenceType::getUnknownExtent()) {
+        hasUnknown = true;
+        break;
+      }
+      rangeBounds.push_back(0);
+      rangeBounds.push_back(dim - 1);
+    }
+    if (!hasUnknown) {
+      mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
+      arrVal =
+          fir::InsertOnRangeOp::create(builder, loc, seqTy, arrVal, elePat,
+                                       builder.getIndexVectorAttr(rangeBounds));
+      fir::StoreOp::create(builder, loc, arrVal, base);
+    }
+  } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(storeTy)) {
+    // Derived type: zero the whole struct, or walk fields for nan/snan/hex.
+    if (mode == Fortran::lower::InitLocalKind::Zero) {
+      fir::StoreOp::create(builder, loc,
+                           fir::ZeroOp::create(builder, loc, recTy), base);
+    } else {
+      for (auto [fieldName, fieldTy] : recTy.getTypeList()) {
+        auto fieldIdx = fir::FieldIndexOp::create(
+            builder, loc, fir::FieldType::get(recTy.getContext()), fieldName,
+            recTy, mlir::ValueRange{});
+        mlir::Value fieldAddr =
+            fir::CoordinateOp::create(builder, loc, builder.getRefType(fieldTy),
+                                      base, mlir::ValueRange{fieldIdx});
+        genInitLocalStore(builder, loc, fieldTy, fieldAddr, mode, hexByte);
+      }
+    }
+  } else {
+    // Scalar (integer, real, complex, logical, character): store directly.
+    genInitLocalStore(builder, loc, storeTy, base, mode, hexByte);
+  }
+}
+
 /// Instantiate a local variable. Precondition: Each variable will be visited
 /// such that if its properties depend on other variables, the variables upon
 /// which its properties depend will already have been visited.
@@ -1273,6 +1507,8 @@ static void instantiateLocal(Fortran::lower::AbstractConverter &converter,
   if (mustBeDefaultInitializedAtRuntime(var))
     Fortran::lower::defaultInitializeAtRuntime(converter, var.getSymbol(),
                                                symMap);
+  else
+    genInitLocal(converter, var, symMap);
   auto *builder = &converter.getFirOpBuilder();
   bool needsHostCudaCleanup = needCUDAAlloc(var.getSymbol()) &&
                               !cuf::isCUDADeviceContext(builder->getRegion());
diff --git a/flang/test/Driver/finit-local.f90 b/flang/test/Driver/finit-local.f90
new file mode 100644
index 0000000000000..01f288b7a8803
--- /dev/null
+++ b/flang/test/Driver/finit-local.f90
@@ -0,0 +1,33 @@
+! Tests that -finit-local= and -finit-local-zero are accepted by the Flang
+! driver and forwarded correctly to -fc1.
+
+! --- Valid values: zero, nan, snan, hex byte ---
+! RUN: %flang -### -S -finit-local=zero  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ZERO  %s
+! RUN: %flang -### -S -finit-local=nan   %s -o - 2>&1 | FileCheck --check-prefix=CHECK-NAN   %s
+! RUN: %flang -### -S -finit-local=snan  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-SNAN  %s
+! RUN: %flang -### -S -finit-local=0xAA  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-HEX   %s
+! RUN: %flang -### -S -finit-local=0xff  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-HEX2  %s
+
+! --- GFortran alias: -finit-local-zero ---
+! RUN: %flang -### -S -finit-local-zero  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ALIAS %s
+
+! --- Compiler (fc1) directly accepts -finit-local= ---
+! RUN: %flang_fc1 -emit-hlfir -finit-local=zero  %s -o -
+! RUN: %flang_fc1 -emit-hlfir -finit-local=nan   %s -o -
+! RUN: %flang_fc1 -emit-hlfir -finit-local=snan  %s -o -
+! RUN: %flang_fc1 -emit-hlfir -finit-local=0xAA  %s -o -
+! RUN: %flang_fc1 -emit-hlfir -finit-local-zero  %s -o -
+
+! --- Invalid value should produce a diagnostic (fc1 level) ---
+! RUN: not %flang_fc1 -emit-hlfir -finit-local=bogus %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+
+! CHECK-ZERO:  "-fc1"{{.*}}"-finit-local=zero"
+! CHECK-NAN:   "-fc1"{{.*}}"-finit-local=nan"
+! CHECK-SNAN:  "-fc1"{{.*}}"-finit-local=snan"
+! CHECK-HEX:   "-fc1"{{.*}}"-finit-local=0xAA"
+! CHECK-HEX2:  "-fc1"{{.*}}"-finit-local=0xff"
+! CHECK-ALIAS: "-fc1"{{.*}}"-finit-local=zero"
+! CHECK-ERR:   error: invalid value 'bogus' in '-finit-local=bogus'
+
+subroutine dummy_sub()
+end subroutine
diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90
new file mode 100644
index 0000000000000..a59322c6df139
--- /dev/null
+++ b/flang/test/Lower/finit-local-f128.f90
@@ -0,0 +1,64 @@
+! Tests for -finit-local= with REAL(16) and COMPLEX(16) (IEEE f128).
+! These types require f128 math support, which is not available on AIX.
+!
+! REQUIRES: flang-supports-f128-math
+!
+! RUN: bbc -emit-hlfir -finit-local=zero  -o - %s | FileCheck --check-prefix=ZERO  %s
+! RUN: bbc -emit-hlfir -finit-local=nan   -o - %s | FileCheck --check-prefix=NAN   %s
+! RUN: bbc -emit-hlfir -finit-local=snan  -o - %s | FileCheck --check-prefix=SNAN  %s
+! RUN: bbc -emit-hlfir -finit-local=0xAA  -o - %s | FileCheck --check-prefix=HEX   %s
+
+! ---------------------------------------------------------------------------
+! REAL(16) -- 16-byte FP (f128); hex uses 128-bit APInt splat + bitcast
+! 0xAA * 16 bytes = -113427455640312821154458202477256070486 (signed i128)
+! ---------------------------------------------------------------------------
+subroutine test_real16(res)
+  real(16) :: res
+  real(16) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_real16
+! ZERO: fir.zero_bits f128
+! ZERO: fir.store {{.*}} : !fir.ref<f128>
+
+! NAN-LABEL:  func.func @_QPtest_real16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref<f128>
+
+! SNAN-LABEL: func.func @_QPtest_real16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref<f128>
+
+! HEX-LABEL:  func.func @_QPtest_real16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  fir.store {{.*}} : !fir.ref<f128>
+
+! ---------------------------------------------------------------------------
+! COMPLEX(16) -- two f128 parts; hex uses 128-bit APInt splat + bitcast
+! 0xAA * 16 bytes = -113427455640312821154458202477256070486 (signed i128)
+! ---------------------------------------------------------------------------
+subroutine test_complex16(res)
+  complex(16) :: res
+  complex(16) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_complex16
+! ZERO: fir.zero_bits !fir.complex<16>
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+
+! NAN-LABEL:  func.func @_QPtest_complex16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  complex.create {{.*}}, {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+
+! SNAN-LABEL: func.func @_QPtest_complex16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: complex.create {{.*}}, {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+
+! HEX-LABEL:  func.func @_QPtest_complex16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  complex.create {{.*}}, {{.*}} : f128
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.complex<16>>
diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90
new file mode 100644
index 0000000000000..379a7842dd0ac
--- /dev/null
+++ b/flang/test/Lower/finit-local.f90
@@ -0,0 +1,496 @@
+! Tests for -finit-local= local variable initialization.
+!
+! Covers every Fortran type listed in the RFC type-mapping table:
+!   INTEGER(k) k=1,2,4,8
+!   REAL(k)    k=4,8  (k=16 in finit-local-f128.f90, requires flang-supports-f128-math)
+!   COMPLEX(k) k=4,8  (k=16 in finit-local-f128.f90, requires flang-supports-f128-math)
+!   LOGICAL(k) k=1,4
+!   CHARACTER(n)
+!   Derived type (struct with plain-int and real components)
+!   Arrays of integer and real
+!
+! Modes exercised: zero, nan, snan, 0xAA (hex), and off (no flag).
+!
+! RUN: bbc -emit-hlfir -finit-local=zero  -o - %s | FileCheck --check-prefix=ZERO  %s
+! RUN: bbc -emit-hlfir -finit-local=nan   -o - %s | FileCheck --check-prefix=NAN   %s
+! RUN: bbc -emit-hlfir -finit-local=snan  -o - %s | FileCheck --check-prefix=SNAN  %s
+! RUN: bbc -emit-hlfir -finit-local=0xAA  -o - %s | FileCheck --check-prefix=HEX   %s
+! RUN: bbc -emit-hlfir                    -o - %s | FileCheck --check-prefix=OFF   %s
+! RUN: bbc -emit-hlfir -finit-local-zero  -o - %s | FileCheck --check-prefix=ZERO  %s
+
+! ---------------------------------------------------------------------------
+! INTEGER(1) -- 1-byte: pattern 0xAA = -86 (signed) = 170 (unsigned)
+! ---------------------------------------------------------------------------
+subroutine test_int1(res)
+  integer(1) :: res
+  integer(1) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int1
+! ZERO: fir.alloca i8
+! ZERO: fir.zero_bits i8
+! ZERO: fir.store {{.*}} : !fir.ref<i8>
+
+! NAN-LABEL:  func.func @_QPtest_int1
+! NAN:  arith.constant -86 : i8
+! NAN:  fir.store {{.*}} : !fir.ref<i8>
+
+! HEX-LABEL:  func.func @_QPtest_int1
+! HEX:  arith.constant -86 : i8
+! HEX:  fir.store {{.*}} : !fir.ref<i8>
+
+! OFF-LABEL: func.func @_QPtest_int1
+! OFF-NOT: fir.store {{.*}} : !fir.ref<i8>
+
+! ---------------------------------------------------------------------------
+! INTEGER(2) -- 2-byte: pattern 0xAAAA = -21846 (signed)
+! ---------------------------------------------------------------------------
+subroutine test_int2(res)
+  integer(2) :: res
+  integer(2) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int2
+! ZERO: fir.zero_bits i16
+
+! NAN-LABEL:  func.func @_QPtest_int2
+! NAN:  arith.constant -21846 : i16
+! NAN:  fir.store {{.*}} : !fir.ref<i16>
+
+! HEX-LABEL:  func.func @_QPtest_int2
+! HEX:  arith.constant -21846 : i16
+! HEX:  fir.store {{.*}} : !fir.ref<i16>
+
+! ---------------------------------------------------------------------------
+! INTEGER(4) -- 4-byte: pattern 0xAAAAAAAA = -1431655766 (signed)
+! ---------------------------------------------------------------------------
+subroutine test_int4(res)
+  integer(4) :: res
+  integer(4) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int4
+! ZERO: fir.zero_bits i32
+
+! NAN-LABEL:  func.func @_QPtest_int4
+! NAN:  arith.constant -1431655766 : i32
+! NAN:  fir.store {{.*}} : !fir.ref<i32>
+
+! SNAN-LABEL: func.func @_QPtest_int4
+! SNAN: arith.constant -1431655766 : i32
+! SNAN: fir.store {{.*}} : !fir.ref<i32>
+
+! HEX-LABEL:  func.func @_QPtest_int4
+! HEX:  arith.constant -1431655766 : i32
+! HEX:  fir.store {{.*}} : !fir.ref<i32>
+
+! OFF-LABEL: func.func @_QPtest_int4
+! OFF-NOT: fir.zero_bits
+
+! ---------------------------------------------------------------------------
+! INTEGER(8) -- 8-byte: pattern 0xAAAAAAAAAAAAAAAA = -6148914691236517206 (signed)
+! ---------------------------------------------------------------------------
+subroutine test_int8(res)
+  integer(8) :: res
+  integer(8) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int8
+! ZERO: fir.zero_bits i64
+
+! NAN-LABEL:  func.func @_QPtest_int8
+! NAN:  arith.constant -6148914691236517206 : i64
+! NAN:  fir.store {{.*}} : !fir.ref<i64>
+
+! HEX-LABEL:  func.func @_QPtest_int8
+! HEX:  arith.constant -6148914691236517206 : i64
+! HEX:  fir.store {{.*}} : !fir.ref<i64>
+
+! ---------------------------------------------------------------------------
+! REAL(4) -- zero fills with fir.zero_bits; nan/snan with FP constant; hex bitcast
+! ---------------------------------------------------------------------------
+subroutine test_real4(res)
+  real(4) :: res
+  real(4) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_real4
+! ZERO: fir.zero_bits f32
+! ZERO: fir.store {{.*}} : !fir.ref<f32>
+
+! NAN-LABEL:  func.func @_QPtest_real4
+! NAN:  arith.constant {{.*}} : f32
+! NAN:  fir.store {{.*}} : !fir.ref<f32>
+
+! SNAN-LABEL: func.func @_QPtest_real4
+! SNAN: arith.constant {{.*}} : f32
+! SNAN: fir.store {{.*}} : !fir.ref<f32>
+
+! HEX-LABEL:  func.func @_QPtest_real4
+! HEX:  arith.constant -1431655766 : i32
+! HEX:  arith.bitcast {{.*}} : i32 to f32
+! HEX:  fir.store {{.*}} : !fir.ref<f32>
+
+! OFF-LABEL: func.func @_QPtest_real4
+! OFF-NOT: fir.zero_bits
+
+! ---------------------------------------------------------------------------
+! REAL(8) -- 8-byte FP
+! ---------------------------------------------------------------------------
+subroutine test_real8(res)
+  real(8) :: res
+  real(8) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_real8
+! ZERO: fir.zero_bits f64
+! ZERO: fir.store {{.*}} : !fir.ref<f64>
+
+! NAN-LABEL:  func.func @_QPtest_real8
+! NAN:  arith.constant {{.*}} : f64
+! NAN:  fir.store {{.*}} : !fir.ref<f64>
+
+! SNAN-LABEL: func.func @_QPtest_real8
+! SNAN: arith.constant {{.*}} : f64
+! SNAN: fir.store {{.*}} : !fir.ref<f64>
+
+! HEX-LABEL:  func.func @_QPtest_real8
+! HEX:  arith.constant -6148914691236517206 : i64
+! HEX:  arith.bitcast {{.*}} : i64 to f64
+! HEX:  fir.store {{.*}} : !fir.ref<f64>
+
+! ---------------------------------------------------------------------------
+! COMPLEX(4) -- two f32 parts; stored as complex<f32>
+! nan/snan: both parts get NaN; hex: both parts get bitcast pattern
+! ---------------------------------------------------------------------------
+subroutine test_complex4(res)
+  complex(4) :: res
+  complex(4) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_complex4
+! ZERO: fir.zero_bits complex<f32>
+! ZERO: fir.store {{.*}} : !fir.ref<complex<f32>>
+
+! NAN-LABEL:  func.func @_QPtest_complex4
+! NAN:  arith.constant {{.*}} : f32
+! NAN:  complex.create {{.*}} : complex<f32>
+! NAN:  fir.store {{.*}} : !fir.ref<complex<f32>>
+
+! SNAN-LABEL: func.func @_QPtest_complex4
+! SNAN: arith.constant {{.*}} : f32
+! SNAN: complex.create {{.*}} : complex<f32>
+! SNAN: fir.store {{.*}} : !fir.ref<complex<f32>>
+
+! HEX-LABEL:  func.func @_QPtest_complex4
+! HEX:  arith.constant -1431655766 : i32
+! HEX:  arith.bitcast {{.*}} : i32 to f32
+! HEX:  complex.create {{.*}} : complex<f32>
+! HEX:  fir.store {{.*}} : !fir.ref<complex<f32>>
+
+! ---------------------------------------------------------------------------
+! COMPLEX(8) -- two f64 parts; stored as complex<f64>
+! ---------------------------------------------------------------------------
+subroutine test_complex8(res)
+  complex(8) :: res
+  complex(8) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_complex8
+! ZERO: fir.zero_bits complex<f64>
+! ZERO: fir.store {{.*}} : !fir.ref<complex<f64>>
+
+! NAN-LABEL:  func.func @_QPtest_complex8
+! NAN:  arith.constant {{.*}} : f64
+! NAN:  complex.create {{.*}} : complex<f64>
+! NAN:  fir.store {{.*}} : !fir.ref<complex<f64>>
+
+! SNAN-LABEL: func.func @_QPtest_complex8
+! SNAN: arith.constant {{.*}} : f64
+! SNAN: complex.create {{.*}} : complex<f64>
+! SNAN: fir.store {{.*}} : !fir.ref<complex<f64>>
+
+! HEX-LABEL:  func.func @_QPtest_complex8
+! HEX:  arith.constant -6148914691236517206 : i64
+! HEX:  arith.bitcast {{.*}} : i64 to f64
+! HEX:  complex.create {{.*}} : complex<f64>
+! HEX:  fir.store {{.*}} : !fir.ref<complex<f64>>
+
+! ---------------------------------------------------------------------------
+! LOGICAL(1) -- stored as i8; pattern 0xAA = -86
+! ---------------------------------------------------------------------------
+subroutine test_logical1(res)
+  logical(1) :: res
+  logical(1) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_logical1
+! ZERO: fir.zero_bits !fir.logical<1>
+
+! NAN-LABEL:  func.func @_QPtest_logical1
+! NAN:  fir.zero_bits !fir.logical<1>
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
+
+! HEX-LABEL:  func.func @_QPtest_logical1
+! HEX:  fir.zero_bits !fir.logical<1>
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
+
+! ---------------------------------------------------------------------------
+! LOGICAL(4) -- stored as i32; pattern 0xAAAAAAAA = -1431655766
+! ---------------------------------------------------------------------------
+subroutine test_logical4(res)
+  logical(4) :: res
+  logical(4) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_logical4
+! ZERO: fir.zero_bits !fir.logical<4>
+
+! NAN-LABEL:  func.func @_QPtest_logical4
+! NAN:  fir.zero_bits !fir.logical<4>
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
+
+! HEX-LABEL:  func.func @_QPtest_logical4
+! HEX:  fir.zero_bits !fir.logical<4>
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
+
+! ---------------------------------------------------------------------------
+! CHARACTER(10) -- fir::CharacterType is not mlir::FloatType/IntegerType/ComplexType
+! nan/snan/hex: fall back to fir.zero_bits (known limitation, TODO)
+! ---------------------------------------------------------------------------
+subroutine test_char10(res)
+  character(10) :: res
+  character(10) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_char10
+! ZERO: fir.zero_bits !fir.char<1,10>
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.char<1,10>>
+
+! NAN-LABEL:  func.func @_QPtest_char10
+! NAN:  fir.zero_bits !fir.char<1,10>
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.char<1,10>>
+
+! SNAN-LABEL: func.func @_QPtest_char10
+! SNAN: fir.zero_bits !fir.char<1,10>
+! SNAN: fir.store {{.*}} : !fir.ref<!fir.char<1,10>>
+
+! HEX-LABEL:  func.func @_QPtest_char10
+! HEX:  fir.zero_bits !fir.char<1,10>
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.char<1,10>>
+
+! ---------------------------------------------------------------------------
+! Derived type -- struct with an INTEGER(4) and a REAL(4) field
+! nan/hex: field-by-field walk (integer: 0xAA; real: NaN or bitcast)
+! ---------------------------------------------------------------------------
+subroutine test_derived(res)
+  type :: mytype
+    integer(4) :: i
+    real(4) :: r
+  end type
+  type(mytype) :: res
+  type(mytype) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_derived
+! ZERO: fir.zero_bits !fir.type<{{.*}}>
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.type<{{.*}}>>
+
+! NAN-LABEL:  func.func @_QPtest_derived
+! NAN:  fir.coordinate_of {{.*}} -> !fir.ref<i32>
+! NAN:  arith.constant {{.*}} : i32
+! NAN:  fir.store {{.*}} : !fir.ref<i32>
+! NAN:  fir.coordinate_of {{.*}} -> !fir.ref<f32>
+! NAN:  arith.constant {{.*}} : f32
+! NAN:  fir.store {{.*}} : !fir.ref<f32>
+
+! HEX-LABEL:  func.func @_QPtest_derived
+! HEX:  fir.coordinate_of {{.*}} -> !fir.ref<i32>
+! HEX:  arith.constant {{.*}} : i32
+! HEX:  fir.store {{.*}} : !fir.ref<i32>
+! HEX:  fir.coordinate_of {{.*}} -> !fir.ref<f32>
+! HEX:  arith.bitcast {{.*}} : i32 to f32
+! HEX:  fir.store {{.*}} : !fir.ref<f32>
+
+
+! ---------------------------------------------------------------------------
+! Array INTEGER(4)(4) -- 1-D; filled via insert_on_range
+! ---------------------------------------------------------------------------
+subroutine test_int_array(res)
+  integer(4) :: res(4)
+  integer(4) :: x(4)
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int_array
+! ZERO: fir.insert_on_range {{.*}} from (0) to (3)
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
+
+! NAN-LABEL:  func.func @_QPtest_int_array
+! NAN:  fir.insert_on_range {{.*}} from (0) to (3)
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
+
+! HEX-LABEL:  func.func @_QPtest_int_array
+! HEX:  fir.insert_on_range {{.*}} from (0) to (3)
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
+
+! OFF-LABEL: func.func @_QPtest_int_array
+! OFF-NOT: fir.insert_on_range
+
+! ---------------------------------------------------------------------------
+! Array REAL(4)(4) -- 1-D; nan/snan: NaN element; hex: bitcast element
+! ---------------------------------------------------------------------------
+subroutine test_real_array(res)
+  real(4) :: res(4)
+  real(4) :: x(4)
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_real_array
+! ZERO: fir.insert_on_range {{.*}} from (0) to (3)
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+
+! NAN-LABEL:  func.func @_QPtest_real_array
+! NAN:  fir.insert_on_range {{.*}} from (0) to (3)
+! NAN:  fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+
+! SNAN-LABEL: func.func @_QPtest_real_array
+! SNAN: fir.insert_on_range {{.*}} from (0) to (3)
+! SNAN: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+
+! HEX-LABEL:  func.func @_QPtest_real_array
+! HEX:  fir.insert_on_range {{.*}} from (0) to (3)
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+
+! ---------------------------------------------------------------------------
+! Array INTEGER(4)(3,4) -- 2-D; insert_on_range with two-dimension bounds
+! ---------------------------------------------------------------------------
+subroutine test_int_array_2d(res)
+  integer(4) :: res(3,4)
+  integer(4) :: x(3,4)
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_int_array_2d
+! ZERO: fir.insert_on_range {{.*}} from (0, 0) to (2, 3)
+! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>>
+
+! HEX-LABEL: func.func @_QPtest_int_array_2d
+! HEX:  fir.insert_on_range {{.*}} from (0, 0) to (2, 3)
+! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>>
+
+! ---------------------------------------------------------------------------
+! Exclusion: explicit init (= 42) -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_explicit_init(res)
+  integer(4) :: res
+  integer(4) :: x = 42
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_explicit_init
+! ZERO-NOT: fir.zero_bits
+
+! NAN-LABEL:  func.func @_QPtest_explicit_init
+! NAN-NOT:  arith.constant -1431655766 : i32
+
+! HEX-LABEL:  func.func @_QPtest_explicit_init
+! HEX-NOT:  arith.bitcast
+
+! ---------------------------------------------------------------------------
+! Exclusion: DATA statement init -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_data_init(res)
+  integer(4) :: res
+  integer(4) :: x
+  data x /99/
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_data_init
+! ZERO-NOT: fir.zero_bits i32
+
+! NAN-LABEL:  func.func @_QPtest_data_init
+! NAN-NOT:  arith.constant -1431655766 : i32
+
+! HEX-LABEL:  func.func @_QPtest_data_init
+! HEX-NOT:  arith.bitcast
+
+! ---------------------------------------------------------------------------
+! Exclusion: derived-type default component init -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_default_comp_init(res)
+  type :: inittype
+    integer(4) :: i = 7
+    real(4)    :: r = 3.14
+  end type
+  type(inittype) :: res
+  type(inittype) :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_default_comp_init
+! ZERO-NOT: fir.zero_bits
+
+! NAN-LABEL:  func.func @_QPtest_default_comp_init
+! NAN-NOT:  arith.constant -1431655766 : i32
+
+! HEX-LABEL:  func.func @_QPtest_default_comp_init
+! HEX-NOT:  arith.bitcast
+
+! ---------------------------------------------------------------------------
+! Exclusion: SAVE -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_save(res)
+  integer(4) :: res
+  integer(4), save :: x
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_save
+! ZERO-NOT: fir.zero_bits i32
+
+! HEX-LABEL: func.func @_QPtest_save
+! HEX-NOT: arith.constant -1431655766 : i32
+
+! ---------------------------------------------------------------------------
+! Exclusion: dummy argument -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_dummy(x, res)
+  integer(4), intent(in) :: x
+  integer(4) :: res
+  res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_dummy
+! ZERO-NOT: fir.zero_bits i32
+
+! HEX-LABEL: func.func @_QPtest_dummy
+! HEX-NOT: arith.constant -1431655766 : i32
+
+! ---------------------------------------------------------------------------
+! Exclusion: ALLOCATABLE -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_allocatable(res)
+  integer(4), allocatable :: x
+  integer(4) :: res
+  if (allocated(x)) res = x
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_allocatable
+! ZERO-NOT: fir.zero_bits i32
+
+! HEX-LABEL: func.func @_QPtest_allocatable
+! HEX-NOT: arith.constant -1431655766 : i32
+
+! ---------------------------------------------------------------------------
+! Exclusion: EQUIVALENCE -- must NOT be touched
+! ---------------------------------------------------------------------------
+subroutine test_equivalence(res)
+  integer(4) :: res
+  integer(4) :: x, y
+  equivalence (x, y)
+  res = x + y
+end subroutine
+! ZERO-LABEL: func.func @_QPtest_equivalence
+! ZERO-NOT: fir.zero_bits
+! ZERO: return
+
+! NAN-LABEL:  func.func @_QPtest_equivalence
+! NAN-NOT:  arith.constant -1431655766 : i32
+! NAN: return
+
+! HEX-LABEL:  func.func @_QPtest_equivalence
+! HEX-NOT:  arith.bitcast
+! HEX: return
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 4d6b0a22f426e..0bf66c72c05a7 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -272,6 +272,20 @@ static llvm::cl::opt<bool> initGlobalZero(
     llvm::cl::desc("Zero initialize globals without default initialization"),
     llvm::cl::init(true));
 
+static llvm::cl::opt<std::string>
+    initLocalMode("finit-local",
+                  llvm::cl::desc("Initialize local variables without explicit "
+                                 "or default initialization. "
+                                 "Accepts: zero, nan, snan, or 0x<hex-byte>."),
+                  llvm::cl::init(""));
+
+static llvm::cl::opt<bool> initLocalZero(
+    "finit-local-zero",
+    llvm::cl::desc(
+        "Zero-initialize local variables without explicit or default "
+        "initialization (alias for -finit-local=zero)"),
+    llvm::cl::init(false));
+
 static llvm::cl::opt<bool>
     reallocateLHS("frealloc-lhs",
                   llvm::cl::desc("Follow Fortran 2003 rules for (re)allocating "
@@ -501,6 +515,31 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
   loweringOptions.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder);
   loweringOptions.setIntegerWrapAround(integerWrapAround);
   loweringOptions.setInitGlobalZero(initGlobalZero);
+  // -finit-local-zero (alias for -finit-local=zero)
+  if (initLocalZero)
+    loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
+
+  // -finit-local=
+  if (!initLocalMode.empty()) {
+    llvm::StringRef val = initLocalMode;
+    if (val == "zero") {
+      loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
+    } else if (val == "nan") {
+      loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::QNaN);
+    } else if (val == "snan") {
+      loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::SNaN);
+    } else if (val.starts_with("0x") || val.starts_with("0X")) {
+      unsigned long long hexVal = 0;
+      if (!val.drop_front(2).getAsInteger(16, hexVal) && hexVal <= 0xFF) {
+        loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Hex);
+        loweringOptions.setInitLocalPattern(static_cast<uint8_t>(hexVal));
+      } else {
+        llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";
+      }
+    } else {
+      llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";
+    }
+  }
   loweringOptions.setReallocateLHS(reallocateLHS);
   loweringOptions.setStackRepackArrays(stackRepackArrays);
   loweringOptions.setRepackArrays(repackArrays);

>From ca2f99951fa348c5e5d06fc463695757d83387f4 Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Fri, 14 Aug 2026 10:34:37 -0400
Subject: [PATCH 2/4] To address tarunprabhu's review comments.

---
 clang/include/clang/Options/FlangOptions.td |  4 ---
 flang/docs/ReleaseNotes.md                  |  2 +-
 flang/lib/Lower/ConvertVariable.cpp         | 14 +++++----
 flang/test/Driver/finit-local.f90           | 35 ++++++++-------------
 flang/test/Lower/finit-local-f128.f90       |  8 ++---
 5 files changed, 26 insertions(+), 37 deletions(-)

diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index d3402467b6f8e..881bf58aa1e3f 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -393,10 +393,6 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", "init-global-zero",
   PosFlag<SetTrue, [], [], "Zero initialize globals without default initialization (default)">,
   NegFlag<SetFalse, [], [], "Do not zero initialize globals without default initialization">>;
 
-// -finit-local=<zero|nan|snan|0x<hex>>
-// Initialize automatic (local, stack) variables that have no explicit or
-// default initialization.  -finit-local-zero is a GFortran compatibility alias
-// for -finit-local=zero.
 def finit_local_EQ : Joined<["-"], "finit-local=">,
   Group<f_Group>,
   Visibility<[FC1Option, FlangOption]>,
diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md
index 660166e8099d0..3cc8794785803 100644
--- a/flang/docs/ReleaseNotes.md
+++ b/flang/docs/ReleaseNotes.md
@@ -57,7 +57,7 @@ page](https://llvm.org/releases/).
 - Added `-gz` and `-gz=<format>` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
-- Added `-finit-local=<val>` to initialize automatic (local, stack-allocated)
+- Added `-finit-local=<val>` to initialize automatic local
   variables that have no explicit or default initialization. Accepted values
   are `zero`, `nan`, `snan`, and `0x<hex-byte>` (e.g. `0xAA`). The gfortran
   compatibility alias `-finit-local-zero` is equivalent to `-finit-local=zero`.
diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 03ebe82cb52ce..202222d299982 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -1420,26 +1420,28 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter,
       elePat = genByteSplatInit(builder, loc, eleTy, hexByte);
       break;
     case Fortran::lower::InitLocalKind::QNaN:
-      if (fpTy)
+      if (fpTy) {
         elePat = genFPNaNInit(builder, loc, fpTy, false);
-      else if (cplxTy) {
+      } else if (cplxTy) {
         auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
         mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, false);
         elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
                                                  nanPart);
-      } else
+      } else {
         elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+      }
       break;
     case Fortran::lower::InitLocalKind::SNaN:
-      if (fpTy)
+      if (fpTy) {
         elePat = genFPNaNInit(builder, loc, fpTy, true);
-      else if (cplxTy) {
+      } else if (cplxTy) {
         auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
         mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, true);
         elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
                                                  nanPart);
-      } else
+      } else {
         elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+      }
       break;
     default:
       llvm_unreachable("unexpected InitLocalKind");
diff --git a/flang/test/Driver/finit-local.f90 b/flang/test/Driver/finit-local.f90
index 01f288b7a8803..1d39a83b4ce01 100644
--- a/flang/test/Driver/finit-local.f90
+++ b/flang/test/Driver/finit-local.f90
@@ -2,32 +2,23 @@
 ! driver and forwarded correctly to -fc1.
 
 ! --- Valid values: zero, nan, snan, hex byte ---
-! RUN: %flang -### -S -finit-local=zero  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ZERO  %s
-! RUN: %flang -### -S -finit-local=nan   %s -o - 2>&1 | FileCheck --check-prefix=CHECK-NAN   %s
-! RUN: %flang -### -S -finit-local=snan  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-SNAN  %s
-! RUN: %flang -### -S -finit-local=0xAA  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-HEX   %s
-! RUN: %flang -### -S -finit-local=0xff  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-HEX2  %s
+! RUN: %flang -### -S -finit-local=zero  %s 2>&1 | FileCheck --check-prefix=ZERO  %s
+! RUN: %flang -### -S -finit-local=nan   %s 2>&1 | FileCheck --check-prefix=NAN   %s
+! RUN: %flang -### -S -finit-local=snan  %s 2>&1 | FileCheck --check-prefix=SNAN  %s
+! RUN: %flang -### -S -finit-local=0xAA  %s 2>&1 | FileCheck --check-prefix=HEX   %s
+! RUN: %flang -### -S -finit-local=0xff  %s 2>&1 | FileCheck --check-prefix=HEX2  %s
 
 ! --- GFortran alias: -finit-local-zero ---
-! RUN: %flang -### -S -finit-local-zero  %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ALIAS %s
-
-! --- Compiler (fc1) directly accepts -finit-local= ---
-! RUN: %flang_fc1 -emit-hlfir -finit-local=zero  %s -o -
-! RUN: %flang_fc1 -emit-hlfir -finit-local=nan   %s -o -
-! RUN: %flang_fc1 -emit-hlfir -finit-local=snan  %s -o -
-! RUN: %flang_fc1 -emit-hlfir -finit-local=0xAA  %s -o -
-! RUN: %flang_fc1 -emit-hlfir -finit-local-zero  %s -o -
-
+! RUN: %flang -### -S -finit-local-zero  %s 2>&1 | FileCheck --check-prefix=ZERO %s
 ! --- Invalid value should produce a diagnostic (fc1 level) ---
-! RUN: not %flang_fc1 -emit-hlfir -finit-local=bogus %s -o - 2>&1 | FileCheck --check-prefix=CHECK-ERR %s
+! RUN: not %flang_fc1 -emit-hlfir -finit-local=bogus %s 2>&1 | FileCheck --check-prefix=ERR %s
 
-! CHECK-ZERO:  "-fc1"{{.*}}"-finit-local=zero"
-! CHECK-NAN:   "-fc1"{{.*}}"-finit-local=nan"
-! CHECK-SNAN:  "-fc1"{{.*}}"-finit-local=snan"
-! CHECK-HEX:   "-fc1"{{.*}}"-finit-local=0xAA"
-! CHECK-HEX2:  "-fc1"{{.*}}"-finit-local=0xff"
-! CHECK-ALIAS: "-fc1"{{.*}}"-finit-local=zero"
-! CHECK-ERR:   error: invalid value 'bogus' in '-finit-local=bogus'
+! ZERO:  "-fc1"{{.*}} "-finit-local=zero"
+! NAN:   "-fc1"{{.*}} "-finit-local=nan"
+! SNAN:  "-fc1"{{.*}} "-finit-local=snan"
+! HEX:   "-fc1"{{.*}} "-finit-local=0xAA"
+! HEX2:  "-fc1"{{.*}} "-finit-local=0xff"
+! ERR:   error: invalid value 'bogus' in '-finit-local=bogus'
 
 subroutine dummy_sub()
 end subroutine
diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90
index a59322c6df139..69b6af69e4cee 100644
--- a/flang/test/Lower/finit-local-f128.f90
+++ b/flang/test/Lower/finit-local-f128.f90
@@ -3,10 +3,10 @@
 !
 ! REQUIRES: flang-supports-f128-math
 !
-! RUN: bbc -emit-hlfir -finit-local=zero  -o - %s | FileCheck --check-prefix=ZERO  %s
-! RUN: bbc -emit-hlfir -finit-local=nan   -o - %s | FileCheck --check-prefix=NAN   %s
-! RUN: bbc -emit-hlfir -finit-local=snan  -o - %s | FileCheck --check-prefix=SNAN  %s
-! RUN: bbc -emit-hlfir -finit-local=0xAA  -o - %s | FileCheck --check-prefix=HEX   %s
+! RUN: %flang_fc1 -emit-hlfir -finit-local=zero  %s -o - | FileCheck --check-prefix=ZERO  %s
+! RUN: %flang_fc1 -emit-hlfir -finit-local=nan   %s -o - | FileCheck --check-prefix=NAN   %s
+! RUN: %flang_fc1 -emit-hlfir -finit-local=snan  %s -o - | FileCheck --check-prefix=SNAN  %s
+! RUN: %flang_fc1 -emit-hlfir -finit-local=0xAA  %s -o - | FileCheck --check-prefix=HEX   %s
 
 ! ---------------------------------------------------------------------------
 ! REAL(16) -- 16-byte FP (f128); hex uses 128-bit APInt splat + bitcast

>From 511f35b5cf4df8065f248dc35887868b39a13f40 Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Fri, 14 Aug 2026 13:14:20 -0400
Subject: [PATCH 3/4] [flang] Fix -finit-local= issues found in first review
 round

- Refactor genInitLocal to use a recursive initAddr lambda so that
  array-valued derived-type fields dispatch correctly through the array
  path instead of having their sequence type stripped.
- Skip zero-extent arrays in shouldInitLocal. Exclude all CUDA storage.
- Add LOGICAL byte-splat support in genByteSplatInit.
- bbc: make -finit-local-zero and -finit-local= last-wins; invalid
  values now return a non-zero exit code.
- finit-local-f128.f90: accept both !fir.complex<16> and complex<f128>.
---
 flang/lib/Lower/ConvertVariable.cpp   | 178 +++++++++++++++-----------
 flang/test/Lower/finit-local-f128.f90 |  10 +-
 flang/test/Lower/finit-local.f90      |  12 +-
 flang/tools/bbc/bbc.cpp               |  14 +-
 4 files changed, 122 insertions(+), 92 deletions(-)

diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 202222d299982..bbb65619935f4 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -1280,6 +1280,10 @@ static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) {
       return false;
   if (Fortran::semantics::FindEquivalenceSet(sym))
     return false;
+  // CUDA device/managed/unified/shared/pinned variables must not be
+  // initialized with a plain host store; their storage lives in device memory.
+  if (Fortran::semantics::GetCUDADataAttr(&sym))
+    return false;
   return true;
 }
 
@@ -1316,6 +1320,11 @@ static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
     return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal,
                                            partVal);
   }
+  // LOGICAL(k) has a fixed size of k bytes; treat it like an integer splat.
+  if (auto logTy = mlir::dyn_cast<fir::LogicalType>(eleTy)) {
+    mlir::Value intCst = makeIntCst(logTy.getFKind() * 8);
+    return builder.createConvert(loc, logTy, intCst);
+  }
   // TODO: CHARACTER falls back to zero; a future improvement should fill each
   // storage unit with the byte pattern.
   return fir::ZeroOp::create(builder, loc, eleTy);
@@ -1406,84 +1415,97 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter,
   mlir::Value base = fir::getBase(exv);
   mlir::Type storeTy = fir::unwrapRefType(base.getType());
 
-  if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(storeTy)) {
-    // Array: build element constant and use insert_on_range.
-    mlir::Type eleTy = seqTy.getEleTy();
-    auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy);
-    auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy);
-    mlir::Value elePat;
-    switch (mode) {
-    case Fortran::lower::InitLocalKind::Zero:
-      elePat = fir::ZeroOp::create(builder, loc, eleTy);
-      break;
-    case Fortran::lower::InitLocalKind::Hex:
-      elePat = genByteSplatInit(builder, loc, eleTy, hexByte);
-      break;
-    case Fortran::lower::InitLocalKind::QNaN:
-      if (fpTy) {
-        elePat = genFPNaNInit(builder, loc, fpTy, false);
-      } else if (cplxTy) {
-        auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
-        mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, false);
-        elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
-                                                 nanPart);
-      } else {
-        elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
-      }
-      break;
-    case Fortran::lower::InitLocalKind::SNaN:
-      if (fpTy) {
-        elePat = genFPNaNInit(builder, loc, fpTy, true);
-      } else if (cplxTy) {
-        auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
-        mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, true);
-        elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
-                                                 nanPart);
-      } else {
-        elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
-      }
-      break;
-    default:
-      llvm_unreachable("unexpected InitLocalKind");
-    }
-    // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector.
-    llvm::SmallVector<int64_t> rangeBounds;
-    bool hasUnknown = false;
-    for (auto dim : seqTy.getShape()) {
-      if (dim == fir::SequenceType::getUnknownExtent()) {
-        hasUnknown = true;
-        break;
-      }
-      rangeBounds.push_back(0);
-      rangeBounds.push_back(dim - 1);
-    }
-    if (!hasUnknown) {
-      mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
-      arrVal =
-          fir::InsertOnRangeOp::create(builder, loc, seqTy, arrVal, elePat,
-                                       builder.getIndexVectorAttr(rangeBounds));
-      fir::StoreOp::create(builder, loc, arrVal, base);
-    }
-  } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(storeTy)) {
-    // Derived type: zero the whole struct, or walk fields for nan/snan/hex.
-    if (mode == Fortran::lower::InitLocalKind::Zero) {
-      fir::StoreOp::create(builder, loc,
-                           fir::ZeroOp::create(builder, loc, recTy), base);
-    } else {
-      for (auto [fieldName, fieldTy] : recTy.getTypeList()) {
-        auto fieldIdx = fir::FieldIndexOp::create(
-            builder, loc, fir::FieldType::get(recTy.getContext()), fieldName,
-            recTy, mlir::ValueRange{});
-        mlir::Value fieldAddr =
-            fir::CoordinateOp::create(builder, loc, builder.getRefType(fieldTy),
-                                      base, mlir::ValueRange{fieldIdx});
-        genInitLocalStore(builder, loc, fieldTy, fieldAddr, mode, hexByte);
-      }
-    }
-  } else {
-    // Scalar (integer, real, complex, logical, character): store directly.
-    genInitLocalStore(builder, loc, storeTy, base, mode, hexByte);
-  }
+  // Recursive helper: dispatch on type to initialize the storage at \p addr
+  // of type \p ty. Handles arrays, derived types, and scalars.
+  std::function<void(mlir::Type, mlir::Value)> initAddr =
+      [&](mlir::Type ty, mlir::Value addr) {
+        if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) {
+          // Array: build element constant and use insert_on_range.
+          mlir::Type eleTy = seqTy.getEleTy();
+          auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy);
+          auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy);
+          mlir::Value elePat;
+          switch (mode) {
+          case Fortran::lower::InitLocalKind::Zero:
+            elePat = fir::ZeroOp::create(builder, loc, eleTy);
+            break;
+          case Fortran::lower::InitLocalKind::Hex:
+            elePat = genByteSplatInit(builder, loc, eleTy, hexByte);
+            break;
+          case Fortran::lower::InitLocalKind::QNaN:
+            if (fpTy) {
+              elePat = genFPNaNInit(builder, loc, fpTy, false);
+            } else if (cplxTy) {
+              auto partFpTy =
+                  mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+              mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, false);
+              elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy,
+                                                       nanPart, nanPart);
+            } else {
+              elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+            }
+            break;
+          case Fortran::lower::InitLocalKind::SNaN:
+            if (fpTy) {
+              elePat = genFPNaNInit(builder, loc, fpTy, true);
+            } else if (cplxTy) {
+              auto partFpTy =
+                  mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+              mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, true);
+              elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy,
+                                                       nanPart, nanPart);
+            } else {
+              elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+            }
+            break;
+          default:
+            llvm_unreachable("unexpected InitLocalKind");
+          }
+          // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector.
+          llvm::SmallVector<int64_t> rangeBounds;
+          // Skip CHARACTER arrays: fir.zero_bits is not a valid insert_on_range
+          // element for character types (runtime-length or fixed). CHARACTER
+          // initialization is a known TODO.
+          bool hasUnknown = mlir::isa<fir::CharacterType>(eleTy);
+          for (auto dim : seqTy.getShape()) {
+            if (dim == fir::SequenceType::getUnknownExtent() || dim == 0) {
+              hasUnknown = true;
+              break;
+            }
+            rangeBounds.push_back(0);
+            rangeBounds.push_back(dim - 1);
+          }
+          if (!hasUnknown) {
+            mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
+            arrVal = fir::InsertOnRangeOp::create(
+                builder, loc, seqTy, arrVal, elePat,
+                builder.getIndexVectorAttr(rangeBounds));
+            fir::StoreOp::create(builder, loc, arrVal, addr);
+          }
+        } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) {
+          // Derived type: zero the whole struct, or walk fields for
+          // nan/snan/hex.
+          if (mode == Fortran::lower::InitLocalKind::Zero) {
+            fir::StoreOp::create(
+                builder, loc, fir::ZeroOp::create(builder, loc, recTy), addr);
+          } else {
+            for (auto [fieldName, fieldTy] : recTy.getTypeList()) {
+              auto fieldIdx = fir::FieldIndexOp::create(
+                  builder, loc, fir::FieldType::get(recTy.getContext()),
+                  fieldName, recTy, mlir::ValueRange{});
+              mlir::Value fieldAddr = fir::CoordinateOp::create(
+                  builder, loc, builder.getRefType(fieldTy), addr,
+                  mlir::ValueRange{fieldIdx});
+              initAddr(fieldTy, fieldAddr);
+            }
+          }
+        } else {
+          // Scalar (integer, real, complex, logical, character): store
+          // directly.
+          genInitLocalStore(builder, loc, ty, addr, mode, hexByte);
+        }
+      };
+  initAddr(storeTy, base);
 }
 
 /// Instantiate a local variable. Precondition: Each variable will be visited
diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90
index 69b6af69e4cee..b8157e8dbd1e8 100644
--- a/flang/test/Lower/finit-local-f128.f90
+++ b/flang/test/Lower/finit-local-f128.f90
@@ -44,21 +44,21 @@ subroutine test_complex16(res)
   res = x
 end subroutine
 ! ZERO-LABEL: func.func @_QPtest_complex16
-! ZERO: fir.zero_bits !fir.complex<16>
-! ZERO: fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+! ZERO: fir.zero_bits {{!fir\.complex<16>|complex<f128>}}
+! ZERO: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
 
 ! NAN-LABEL:  func.func @_QPtest_complex16
 ! NAN:  arith.constant {{.*}} : f128
 ! NAN:  complex.create {{.*}}, {{.*}} : f128
-! NAN:  fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+! NAN:  fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
 
 ! SNAN-LABEL: func.func @_QPtest_complex16
 ! SNAN: arith.constant {{.*}} : f128
 ! SNAN: complex.create {{.*}}, {{.*}} : f128
-! SNAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+! SNAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
 
 ! HEX-LABEL:  func.func @_QPtest_complex16
 ! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
 ! HEX:  arith.bitcast {{.*}} : i128 to f128
 ! HEX:  complex.create {{.*}}, {{.*}} : f128
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.complex<16>>
+! HEX:  fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90
index 379a7842dd0ac..fe2a4adca4066 100644
--- a/flang/test/Lower/finit-local.f90
+++ b/flang/test/Lower/finit-local.f90
@@ -228,11 +228,13 @@ subroutine test_logical1(res)
 ! ZERO: fir.zero_bits !fir.logical<1>
 
 ! NAN-LABEL:  func.func @_QPtest_logical1
-! NAN:  fir.zero_bits !fir.logical<1>
+! NAN:  arith.constant -86 : i8
+! NAN:  fir.convert {{.*}} : (i8) -> !fir.logical<1>
 ! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
 
 ! HEX-LABEL:  func.func @_QPtest_logical1
-! HEX:  fir.zero_bits !fir.logical<1>
+! HEX:  arith.constant {{.*}} : i8
+! HEX:  fir.convert {{.*}} : (i8) -> !fir.logical<1>
 ! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
 
 ! ---------------------------------------------------------------------------
@@ -247,11 +249,13 @@ subroutine test_logical4(res)
 ! ZERO: fir.zero_bits !fir.logical<4>
 
 ! NAN-LABEL:  func.func @_QPtest_logical4
-! NAN:  fir.zero_bits !fir.logical<4>
+! NAN:  arith.constant -1431655766 : i32
+! NAN:  fir.convert {{.*}} : (i32) -> !fir.logical<4>
 ! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
 
 ! HEX-LABEL:  func.func @_QPtest_logical4
-! HEX:  fir.zero_bits !fir.logical<4>
+! HEX:  arith.constant {{.*}} : i32
+! HEX:  fir.convert {{.*}} : (i32) -> !fir.logical<4>
 ! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
 
 ! ---------------------------------------------------------------------------
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 0bf66c72c05a7..4adbb5e7764d4 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -515,11 +515,8 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
   loweringOptions.setNoPPCNativeVecElemOrder(enableNoPPCNativeVecElemOrder);
   loweringOptions.setIntegerWrapAround(integerWrapAround);
   loweringOptions.setInitGlobalZero(initGlobalZero);
-  // -finit-local-zero (alias for -finit-local=zero)
-  if (initLocalZero)
-    loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
-
-  // -finit-local=
+  // -finit-local= and -finit-local-zero: last occurrence on the command
+  // line wins. Use getPosition() to determine which came last.
   if (!initLocalMode.empty()) {
     llvm::StringRef val = initLocalMode;
     if (val == "zero") {
@@ -535,11 +532,18 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
         loweringOptions.setInitLocalPattern(static_cast<uint8_t>(hexVal));
       } else {
         llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";
+        return mlir::failure();
       }
     } else {
       llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";
+      return mlir::failure();
     }
   }
+  // If -finit-local-zero appears after -finit-local= on the command line,
+  // it overrides; otherwise -finit-local= already set the mode above.
+  if (initLocalZero &&
+      initLocalZero.getPosition() > initLocalMode.getPosition())
+    loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
   loweringOptions.setReallocateLHS(reallocateLHS);
   loweringOptions.setStackRepackArrays(stackRepackArrays);
   loweringOptions.setRepackArrays(repackArrays);

>From 9a1d4ee51075373432a9b47d1ec72ce576e52b3f Mon Sep 17 00:00:00 2001
From: Daniel Chen <cdchen at ca.ibm.com>
Date: Sat, 15 Aug 2026 11:05:00 -0400
Subject: [PATCH 4/4] [flang] Fix -finit-local= correctness issues found in
 review

- LOGICAL: store the raw iN value via a bitcasted address; fir.convert
  to !fir.logical normalizes any nonzero integer to 1.
- Arrays: use fir.do_loop + fir.coordinate_of for non-zero modes to
  avoid llvm.mlir.constant rejecting non-zero ArrayAttr; skip
  zero-extent and CHARACTER arrays.
- CUDA: allow pinned/unified initialization; exclude only device,
  managed, constant, shared, and usedevice storage.
- bbc: reject an explicitly empty -finit-local= value with non-zero
  exit code.
- finit-local-f128.f90: require complex<f128> exactly.
- Add finit-local-logical-llvm.f90, finit-local-array-llvm.f90,
  finit-local-cuda.cuf regression tests.
---
 flang/lib/Lower/ConvertVariable.cpp           | 99 ++++++++++++++-----
 flang/test/Lower/CUDA/finit-local-cuda.cuf    | 40 ++++++++
 flang/test/Lower/finit-local-array-llvm.f90   | 48 +++++++++
 flang/test/Lower/finit-local-f128.f90         | 16 +--
 flang/test/Lower/finit-local-logical-llvm.f90 | 46 +++++++++
 flang/test/Lower/finit-local.f90              | 52 ++++++----
 flang/tools/bbc/bbc.cpp                       |  6 +-
 7 files changed, 253 insertions(+), 54 deletions(-)
 create mode 100644 flang/test/Lower/CUDA/finit-local-cuda.cuf
 create mode 100644 flang/test/Lower/finit-local-array-llvm.f90
 create mode 100644 flang/test/Lower/finit-local-logical-llvm.f90

diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index bbb65619935f4..c1a3df42d1690 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -1280,15 +1280,25 @@ static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) {
       return false;
   if (Fortran::semantics::FindEquivalenceSet(sym))
     return false;
-  // CUDA device/managed/unified/shared/pinned variables must not be
-  // initialized with a plain host store; their storage lives in device memory.
-  if (Fortran::semantics::GetCUDADataAttr(&sym))
-    return false;
+  // Skip CUDA variables whose storage is not host-accessible via a plain
+  // store: device, managed, constant, shared, and usedevice all live in
+  // device memory. Pinned and unified memory are host-accessible and may
+  // be initialized normally.
+  if (auto cudaAttr = Fortran::semantics::GetCUDADataAttr(&sym)) {
+    if (*cudaAttr == Fortran::common::CUDADataAttr::Device ||
+        *cudaAttr == Fortran::common::CUDADataAttr::Managed ||
+        *cudaAttr == Fortran::common::CUDADataAttr::Constant ||
+        *cudaAttr == Fortran::common::CUDADataAttr::Shared ||
+        *cudaAttr == Fortran::common::CUDADataAttr::UseDevice) {
+      return false;
+    }
+  }
   return true;
 }
 
 /// Build a constant whose every byte equals \p bytePat.
 /// FP types: bitcast from an integer splat. Complex: apply to both parts.
+/// LOGICAL(k): returns a raw iN integer (caller stores via bitcasted address).
 /// Character: falls back to fir.zero_bits (see TODO). Derived types are
 /// handled by the caller before this function is reached.
 static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
@@ -1320,10 +1330,11 @@ static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
     return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal,
                                            partVal);
   }
-  // LOGICAL(k) has a fixed size of k bytes; treat it like an integer splat.
+  // LOGICAL(k) has a fixed size of k bytes. Return the raw integer splat;
+  // the caller stores it via a bitcasted address to preserve the bit pattern
+  // (fir.convert from integer to !fir.logical normalizes nonzero -> true).
   if (auto logTy = mlir::dyn_cast<fir::LogicalType>(eleTy)) {
-    mlir::Value intCst = makeIntCst(logTy.getFKind() * 8);
-    return builder.createConvert(loc, logTy, intCst);
+    return makeIntCst(logTy.getFKind() * 8);
   }
   // TODO: CHARACTER falls back to zero; a future improvement should fill each
   // storage unit with the byte pattern.
@@ -1345,8 +1356,9 @@ static mlir::Value genFPNaNInit(fir::FirOpBuilder &builder, mlir::Location loc,
 }
 
 /// Emit a store of the -finit-local= pattern for a single scalar address.
-/// Complex types get NaN on both parts; other non-FP types use 0xAA byte-splat
-/// for nan/snan modes.
+/// Complex types get NaN on both parts; integer/logical non-FP types use a
+/// 0xAA byte-splat for nan/snan modes. LOGICAL stores via a bitcasted integer
+/// address to preserve the raw bit pattern past fir.convert normalization.
 static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc,
                               mlir::Type ty, mlir::Value addr,
                               Fortran::lower::InitLocalKind mode,
@@ -1390,12 +1402,24 @@ static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc,
   default:
     llvm_unreachable("unexpected InitLocalKind in genInitLocalStore");
   }
-  fir::StoreOp::create(builder, loc, val, addr);
+  // For LOGICAL types, genByteSplatInit returns a raw integer to preserve
+  // the bit pattern. Store it via a bitcasted address to avoid fir.convert
+  // normalization (which would reduce any nonzero value to logical true).
+  if (mode != Fortran::lower::InitLocalKind::Zero &&
+      mlir::isa<fir::LogicalType>(ty)) {
+    unsigned bits = mlir::cast<fir::LogicalType>(ty).getFKind() * 8;
+    mlir::Type intRefTy = builder.getRefType(builder.getIntegerType(bits));
+    mlir::Value intAddr = builder.createConvert(loc, intRefTy, addr);
+    fir::StoreOp::create(builder, loc, val, intAddr);
+  } else {
+    fir::StoreOp::create(builder, loc, val, addr);
+  }
 }
 
 /// Initialize all storage of the local variable \p var per -finit-local= mode.
-/// Arrays use insert_on_range. Derived types walk fields for nan/snan/hex.
-/// Scalars store directly.
+/// Arrays: zero mode uses insert_on_range; non-zero modes use a do_loop +
+/// coordinate_of to avoid llvm.mlir.constant rejecting non-zero ArrayAttrs.
+/// Derived types walk fields for nan/snan/hex. Scalars store directly.
 static void genInitLocal(Fortran::lower::AbstractConverter &converter,
                          const Fortran::lower::pft::Variable &var,
                          Fortran::lower::SymMap &symMap) {
@@ -1461,26 +1485,53 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter,
           default:
             llvm_unreachable("unexpected InitLocalKind");
           }
-          // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector.
-          llvm::SmallVector<int64_t> rangeBounds;
-          // Skip CHARACTER arrays: fir.zero_bits is not a valid insert_on_range
-          // element for character types (runtime-length or fixed). CHARACTER
-          // initialization is a known TODO.
+          // Compute static extents; skip arrays with unknown or zero extents,
+          // and CHARACTER arrays (known TODO, pending PR #159788).
           bool hasUnknown = mlir::isa<fir::CharacterType>(eleTy);
+          int64_t totalElems = 1;
           for (auto dim : seqTy.getShape()) {
             if (dim == fir::SequenceType::getUnknownExtent() || dim == 0) {
               hasUnknown = true;
               break;
             }
-            rangeBounds.push_back(0);
-            rangeBounds.push_back(dim - 1);
+            totalElems *= dim;
           }
           if (!hasUnknown) {
-            mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
-            arrVal = fir::InsertOnRangeOp::create(
-                builder, loc, seqTy, arrVal, elePat,
-                builder.getIndexVectorAttr(rangeBounds));
-            fir::StoreOp::create(builder, loc, arrVal, addr);
+            if (mode == Fortran::lower::InitLocalKind::Zero) {
+              // Zero mode: fir.insert_on_range + store works correctly through
+              // LLVM lowering (ZeroAttr is accepted by llvm.mlir.constant).
+              llvm::SmallVector<int64_t> rangeBounds;
+              for (auto dim : seqTy.getShape()) {
+                rangeBounds.push_back(0);
+                rangeBounds.push_back(dim - 1);
+              }
+              mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
+              arrVal = fir::InsertOnRangeOp::create(
+                  builder, loc, seqTy, arrVal, elePat,
+                  builder.getIndexVectorAttr(rangeBounds));
+              fir::StoreOp::create(builder, loc, arrVal, addr);
+            } else {
+              // Non-zero modes: fir.insert_on_range fails at LLVM lowering
+              // because llvm.mlir.constant does not accept ArrayAttr of
+              // non-zero scalars. Use a flat do_loop + coordinate_of instead.
+              mlir::Type idxTy = builder.getIndexType();
+              mlir::Value zero =
+                  builder.createIntegerConstant(loc, idxTy, 0);
+              mlir::Value last =
+                  builder.createIntegerConstant(loc, idxTy, totalElems - 1);
+              mlir::Value one =
+                  builder.createIntegerConstant(loc, idxTy, 1);
+              auto loop = fir::DoLoopOp::create(builder, loc, zero, last, one,
+                                                /*unordered=*/false,
+                                                /*finalCount=*/false);
+              mlir::OpBuilder::InsertionGuard guard(builder);
+              builder.setInsertionPointToStart(loop.getBody());
+              mlir::Value iv = loop.getInductionVar();
+              mlir::Value elemAddr = fir::CoordinateOp::create(
+                  builder, loc, builder.getRefType(eleTy), addr,
+                  mlir::ValueRange{iv});
+              genInitLocalStore(builder, loc, eleTy, elemAddr, mode, hexByte);
+            }
           }
         } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) {
           // Derived type: zero the whole struct, or walk fields for
diff --git a/flang/test/Lower/CUDA/finit-local-cuda.cuf b/flang/test/Lower/CUDA/finit-local-cuda.cuf
new file mode 100644
index 0000000000000..3b45c4242df0f
--- /dev/null
+++ b/flang/test/Lower/CUDA/finit-local-cuda.cuf
@@ -0,0 +1,40 @@
+! Tests that -finit-local= respects CUDA data attributes in shouldInitLocal.
+! Variables in device-only memory (device, managed, constant, shared) must NOT
+! be initialized via a plain host store. Host-accessible storage (pinned,
+! unified) MAY be initialized normally.
+!
+! RUN: bbc -emit-hlfir -fcuda -finit-local=zero -o - %s | FileCheck %s
+
+module finit_cuda_mod
+contains
+
+  ! device: storage lives in device memory -- must NOT be initialized.
+  subroutine test_device(res)
+    real, device :: x
+    real :: res
+    res = x
+  end subroutine
+  ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_device
+  ! CHECK-NOT: fir.zero_bits f32
+
+  ! unified: host-accessible -- MUST be initialized.
+  subroutine test_unified(res)
+    real, unified :: x
+    real :: res
+    res = x
+  end subroutine
+  ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_unified
+  ! CHECK: fir.zero_bits f32
+  ! CHECK: fir.store
+
+  ! pinned (non-allocatable): host-accessible -- MUST be initialized.
+  subroutine test_pinned(res)
+    real, pinned :: x
+    real :: res
+    res = x
+  end subroutine
+  ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_pinned
+  ! CHECK: fir.zero_bits f32
+  ! CHECK: fir.store
+
+end module
diff --git a/flang/test/Lower/finit-local-array-llvm.f90 b/flang/test/Lower/finit-local-array-llvm.f90
new file mode 100644
index 0000000000000..ff777639131e2
--- /dev/null
+++ b/flang/test/Lower/finit-local-array-llvm.f90
@@ -0,0 +1,48 @@
+! Tests that -finit-local= with non-zero patterns produces correct LLVM IR for
+! static arrays and arrays inside derived types. Previously, fir.insert_on_range
+! with a non-zero element failed at LLVM lowering because llvm.mlir.constant
+! does not accept ArrayAttr of non-zero scalars. The fix uses a do_loop +
+! coordinate_of instead, which lowers correctly through to LLVM IR.
+!
+! RUN: %flang_fc1 -emit-llvm -finit-local=0xAA %s -o - | FileCheck --check-prefix=HEX %s
+! RUN: %flang_fc1 -emit-llvm -finit-local=nan  %s -o - | FileCheck --check-prefix=NAN %s
+! RUN: %flang_fc1 -emit-llvm -finit-local=zero %s -o - | FileCheck --check-prefix=ZERO %s
+
+! ---------------------------------------------------------------------------
+! Static 1-D array INTEGER(4)(4)
+! ---------------------------------------------------------------------------
+subroutine test_int_array(res)
+  integer(4) :: res(4)
+  integer(4) :: x(4)
+  res = x
+end subroutine
+! HEX-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}(
+! HEX:  store i32 -1431655766,
+! HEX-NOT: store i32 0,
+
+! NAN-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}(
+! NAN:  store i32 -1431655766,
+
+! ZERO-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}(
+! ZERO: store [4 x i32] zeroinitializer,
+
+! ---------------------------------------------------------------------------
+! Derived type with an array-valued field  (Thread 2 regression)
+! type t; integer :: a(2); end type; type(t) :: x
+! ---------------------------------------------------------------------------
+subroutine test_array_in_struct(res)
+  type :: t
+    integer(4) :: a(2)
+  end type
+  type(t) :: res
+  type(t) :: x
+  res = x
+end subroutine
+! HEX-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}(
+! HEX:  store i32 -1431655766,
+
+! NAN-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}(
+! NAN:  store i32 -1431655766,
+
+! ZERO-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}(
+! ZERO: store {{.*}} zeroinitializer,
diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90
index b8157e8dbd1e8..937b2e86bb649 100644
--- a/flang/test/Lower/finit-local-f128.f90
+++ b/flang/test/Lower/finit-local-f128.f90
@@ -44,21 +44,21 @@ subroutine test_complex16(res)
   res = x
 end subroutine
 ! ZERO-LABEL: func.func @_QPtest_complex16
-! ZERO: fir.zero_bits {{!fir\.complex<16>|complex<f128>}}
-! ZERO: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
+! ZERO: fir.zero_bits complex<f128>
+! ZERO: fir.store {{.*}} : !fir.ref<complex<f128>>
 
 ! NAN-LABEL:  func.func @_QPtest_complex16
 ! NAN:  arith.constant {{.*}} : f128
-! NAN:  complex.create {{.*}}, {{.*}} : f128
-! NAN:  fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
+! NAN:  complex.create {{.*}}, {{.*}} : complex<f128>
+! NAN:  fir.store {{.*}} : !fir.ref<complex<f128>>
 
 ! SNAN-LABEL: func.func @_QPtest_complex16
 ! SNAN: arith.constant {{.*}} : f128
-! SNAN: complex.create {{.*}}, {{.*}} : f128
-! SNAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
+! SNAN: complex.create {{.*}}, {{.*}} : complex<f128>
+! SNAN: fir.store {{.*}} : !fir.ref<complex<f128>>
 
 ! HEX-LABEL:  func.func @_QPtest_complex16
 ! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
 ! HEX:  arith.bitcast {{.*}} : i128 to f128
-! HEX:  complex.create {{.*}}, {{.*}} : f128
-! HEX:  fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}>
+! HEX:  complex.create {{.*}}, {{.*}} : complex<f128>
+! HEX:  fir.store {{.*}} : !fir.ref<complex<f128>>
diff --git a/flang/test/Lower/finit-local-logical-llvm.f90 b/flang/test/Lower/finit-local-logical-llvm.f90
new file mode 100644
index 0000000000000..6f41b78883b59
--- /dev/null
+++ b/flang/test/Lower/finit-local-logical-llvm.f90
@@ -0,0 +1,46 @@
+! Tests that -finit-local= preserves the requested bit pattern for LOGICAL
+! variables in the final LLVM IR. fir.convert from integer to !fir.logical
+! normalizes any nonzero value to .TRUE. (i.e. 1); the fix stores via a
+! bitcasted integer address instead so the bit pattern is preserved.
+!
+! RUN: %flang_fc1 -emit-llvm -finit-local=0xAA %s -o - | FileCheck --check-prefix=HEX %s
+! RUN: %flang_fc1 -emit-llvm -finit-local=nan  %s -o - | FileCheck --check-prefix=NAN %s
+! RUN: %flang_fc1 -emit-llvm -finit-local=zero %s -o - | FileCheck --check-prefix=ZERO %s
+
+! ---------------------------------------------------------------------------
+! LOGICAL(1) -- 1-byte storage; 0xAA byte-splat = -86 (i8), NOT i8 1
+! ---------------------------------------------------------------------------
+subroutine test_logical1(res)
+  logical(1) :: res
+  logical(1) :: x
+  res = x
+end subroutine
+! HEX-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}(
+! HEX:  store i8 -86,
+! HEX-NOT: store i8 1,
+
+! NAN-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}(
+! NAN:  store i8 -86,
+! NAN-NOT: store i8 1,
+
+! ZERO-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}(
+! ZERO: store i8 0,
+
+! ---------------------------------------------------------------------------
+! LOGICAL(4) -- 4-byte storage; 0xAA byte-splat = -1431655766 (i32), NOT i32 1
+! ---------------------------------------------------------------------------
+subroutine test_logical4(res)
+  logical(4) :: res
+  logical(4) :: x
+  res = x
+end subroutine
+! HEX-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}(
+! HEX:  store i32 -1431655766,
+! HEX-NOT: store i32 1,
+
+! NAN-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}(
+! NAN:  store i32 -1431655766,
+! NAN-NOT: store i32 1,
+
+! ZERO-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}(
+! ZERO: store i32 0,
diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90
index fe2a4adca4066..752b9c4f84c7d 100644
--- a/flang/test/Lower/finit-local.f90
+++ b/flang/test/Lower/finit-local.f90
@@ -17,6 +17,10 @@
 ! RUN: bbc -emit-hlfir -finit-local=0xAA  -o - %s | FileCheck --check-prefix=HEX   %s
 ! RUN: bbc -emit-hlfir                    -o - %s | FileCheck --check-prefix=OFF   %s
 ! RUN: bbc -emit-hlfir -finit-local-zero  -o - %s | FileCheck --check-prefix=ZERO  %s
+! --- Empty value should be rejected by bbc ---
+! RUN: not bbc -emit-hlfir -finit-local=   -o - %s 2>&1 | FileCheck --check-prefix=EMPTY %s
+
+! EMPTY: bbc: invalid -finit-local= value: (empty)
 
 ! ---------------------------------------------------------------------------
 ! INTEGER(1) -- 1-byte: pattern 0xAA = -86 (signed) = 170 (unsigned)
@@ -229,13 +233,13 @@ subroutine test_logical1(res)
 
 ! NAN-LABEL:  func.func @_QPtest_logical1
 ! NAN:  arith.constant -86 : i8
-! NAN:  fir.convert {{.*}} : (i8) -> !fir.logical<1>
-! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
+! NAN:  fir.convert {{.*}} : (!fir.ref<!fir.logical<1>>) -> !fir.ref<i8>
+! NAN:  fir.store {{.*}} : !fir.ref<i8>
 
 ! HEX-LABEL:  func.func @_QPtest_logical1
 ! HEX:  arith.constant {{.*}} : i8
-! HEX:  fir.convert {{.*}} : (i8) -> !fir.logical<1>
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<1>>
+! HEX:  fir.convert {{.*}} : (!fir.ref<!fir.logical<1>>) -> !fir.ref<i8>
+! HEX:  fir.store {{.*}} : !fir.ref<i8>
 
 ! ---------------------------------------------------------------------------
 ! LOGICAL(4) -- stored as i32; pattern 0xAAAAAAAA = -1431655766
@@ -250,13 +254,13 @@ subroutine test_logical4(res)
 
 ! NAN-LABEL:  func.func @_QPtest_logical4
 ! NAN:  arith.constant -1431655766 : i32
-! NAN:  fir.convert {{.*}} : (i32) -> !fir.logical<4>
-! NAN:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
+! NAN:  fir.convert {{.*}} : (!fir.ref<!fir.logical<4>>) -> !fir.ref<i32>
+! NAN:  fir.store {{.*}} : !fir.ref<i32>
 
 ! HEX-LABEL:  func.func @_QPtest_logical4
 ! HEX:  arith.constant {{.*}} : i32
-! HEX:  fir.convert {{.*}} : (i32) -> !fir.logical<4>
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.logical<4>>
+! HEX:  fir.convert {{.*}} : (!fir.ref<!fir.logical<4>>) -> !fir.ref<i32>
+! HEX:  fir.store {{.*}} : !fir.ref<i32>
 
 ! ---------------------------------------------------------------------------
 ! CHARACTER(10) -- fir::CharacterType is not mlir::FloatType/IntegerType/ComplexType
@@ -330,12 +334,14 @@ subroutine test_int_array(res)
 ! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
 
 ! NAN-LABEL:  func.func @_QPtest_int_array
-! NAN:  fir.insert_on_range {{.*}} from (0) to (3)
-! NAN:  fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
+! NAN:  fir.do_loop
+! NAN:  fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xi32>>, index) -> !fir.ref<i32>
+! NAN:  fir.store {{.*}} : !fir.ref<i32>
 
 ! HEX-LABEL:  func.func @_QPtest_int_array
-! HEX:  fir.insert_on_range {{.*}} from (0) to (3)
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<4xi32>>
+! HEX:  fir.do_loop
+! HEX:  fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xi32>>, index) -> !fir.ref<i32>
+! HEX:  fir.store {{.*}} : !fir.ref<i32>
 
 ! OFF-LABEL: func.func @_QPtest_int_array
 ! OFF-NOT: fir.insert_on_range
@@ -353,19 +359,22 @@ subroutine test_real_array(res)
 ! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
 
 ! NAN-LABEL:  func.func @_QPtest_real_array
-! NAN:  fir.insert_on_range {{.*}} from (0) to (3)
-! NAN:  fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+! NAN:  fir.do_loop
+! NAN:  fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32>
+! NAN:  fir.store {{.*}} : !fir.ref<f32>
 
 ! SNAN-LABEL: func.func @_QPtest_real_array
-! SNAN: fir.insert_on_range {{.*}} from (0) to (3)
-! SNAN: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+! SNAN: fir.do_loop
+! SNAN: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32>
+! SNAN: fir.store {{.*}} : !fir.ref<f32>
 
 ! HEX-LABEL:  func.func @_QPtest_real_array
-! HEX:  fir.insert_on_range {{.*}} from (0) to (3)
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<4xf32>>
+! HEX:  fir.do_loop
+! HEX:  fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32>
+! HEX:  fir.store {{.*}} : !fir.ref<f32>
 
 ! ---------------------------------------------------------------------------
-! Array INTEGER(4)(3,4) -- 2-D; insert_on_range with two-dimension bounds
+! Array INTEGER(4)(3,4) -- 2-D; zero uses insert_on_range, hex uses do_loop
 ! ---------------------------------------------------------------------------
 subroutine test_int_array_2d(res)
   integer(4) :: res(3,4)
@@ -377,8 +386,9 @@ subroutine test_int_array_2d(res)
 ! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>>
 
 ! HEX-LABEL: func.func @_QPtest_int_array_2d
-! HEX:  fir.insert_on_range {{.*}} from (0, 0) to (2, 3)
-! HEX:  fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>>
+! HEX:  fir.do_loop
+! HEX:  fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<3x4xi32>>, index) -> !fir.ref<i32>
+! HEX:  fir.store {{.*}} : !fir.ref<i32>
 
 ! ---------------------------------------------------------------------------
 ! Exclusion: explicit init (= 42) -- must NOT be touched
diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp
index 4adbb5e7764d4..8fd72df336ab3 100644
--- a/flang/tools/bbc/bbc.cpp
+++ b/flang/tools/bbc/bbc.cpp
@@ -517,8 +517,12 @@ static llvm::LogicalResult convertFortranSourceToMLIR(
   loweringOptions.setInitGlobalZero(initGlobalZero);
   // -finit-local= and -finit-local-zero: last occurrence on the command
   // line wins. Use getPosition() to determine which came last.
-  if (!initLocalMode.empty()) {
+  if (initLocalMode.getNumOccurrences() > 0) {
     llvm::StringRef val = initLocalMode;
+    if (val.empty()) {
+      llvm::errs() << "bbc: invalid -finit-local= value: (empty)\n";
+      return mlir::failure();
+    }
     if (val == "zero") {
       loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
     } else if (val == "nan") {



More information about the cfe-commits mailing list