[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