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

2026-08-16 Thread Daniel Chen via cfe-commits


@@ -1250,6 +1254,267 @@ 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, vars with explicit or default initialization, and
+/// CUDA variables whose storage is not host-accessible (device, constant,
+/// shared, usedevice).
+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))

DanielCChen wrote:

> Cray pointees pass the eligibility checks in `shouldInitLocal` even though 
> their FIR base is a pointer-box descriptor and they own no storage. You can 
> reproduce this by saving the following as `repro.f90`:
> 
> ```fortran
> integer :: res(10), x(10)
> integer(8) :: p
> pointer (p, x)
> p = loc(res)
> x(3) = 7
> ```
> 
> `flang -fc1 -emit-llvm -O0 -finit-local=zero repro.f90 -o -` emits a 48-byte 
> `memcpy` from null into the pointee descriptor. With `-O2`, the function 
> becomes `unreachable`. Compiling without `-finit-local`, or with an unpatched 
> compiler, keeps the store to `x(3)`.
> 
> Could you exclude Cray pointees in `shouldInitLocal`? Could you reject FIR 
> box types in the scalar fallback? Could you also add `-O0` and `-O2` LLVM 
> regression tests?

Fixed. `shouldInitLocal` now returns `false` for Cray pointees via 
`sym.test(Symbol::Flag::CrayPointee)`. The scalar fallback in `initAddr` guards 
with `!mlir::isa(ty)` to skip any box type that reaches it. 
Tests added in `finit-local-cray-pointee.f90` at `-O0` and `-O2`.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

DanielCChen wrote:

> Managed host storage now initializes correctly.
> 
> In `attributes(global) subroutine test_global_local`, the user-declared `n` 
> still receives `data_attr=device` and remains uninitialized. The only store 
> of zero targets the compiler-created `__builtin_warpsize`.
> 
> Deleting the declaration of `n` and the assignment `n = 42` from the new test 
> leaves all three new CHECK lines passing.
> 
> Could you account for device execution when deciding whether to initialize a 
> local? Could you also make the test verify that the store targets the 
> user-declared `n`?

Here it is as plain text you can copy:

Fixed. `SetImplicitCUDADevice` assigns `device` to every local in a device 
subprogram, so the old check was too broad. `shouldInitLocal` now uses 
`IsCUDADeviceContext(&sym.owner())` to distinguish implicit per-thread stack 
locals (initialize) from explicit global device memory (skip). The test is 
tightened to anchor the `fir.store` to `_QFtest_global_localEn` via 
`hlfir.declare`.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

> The implementation stores the requested values in both fields of a record 
> laid out as `{ i8, i32 }`, but it leaves the three padding bytes untouched.
> 
> Could you either fill every byte of the selected storage or state in the 
> public contract that hex mode covers typed fields rather than every byte?

Fixed for zero and hex modes — zero uses `fir.zero_bits` over the whole struct, 
and `hex` now emits a byte-loop sized by `fir::getTypeSizeAndAlignmentOrCrash`, 
covering all padding bytes; `nan/snan` padding remains a TODO.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

> Static `CHARACTER(0)` initialization now works, but runtime-length 
> `CHARACTER` initialization remains incorrect. For `character(n) :: x`, the 
> generated LLVM IR still allocates `n` bytes and emits one unconditional byte 
> store. When `n = 0` at runtime, the store writes through a zero-byte 
> allocation. Positive lengths initialize only one byte.
> 
> Could you use the runtime length from `ExtendedValue`? Could you also add a 
> test with a runtime length of zero?

Fixed: `character(n)` locals now emit a `fir.do_loop` over `[0, n-1]` 
initializing one byte per iteration, correctly handling both `n=0` (loop 
skipped) and `n>1` (all bytes filled).

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-16 Thread via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

MattPD wrote:

I confirmed that a later `-finit-local-zero` now overrides earlier 
`-finit-local=` options with empty or invalid values.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

MattPD wrote:

Managed host storage now initializes correctly.

In `attributes(global) subroutine test_global_local`, the user-declared `n` 
still receives `data_attr=device` and remains uninitialized. The only store of 
zero targets the compiler-created `__builtin_warpsize`.

Deleting the declaration of `n` and the assignment `n = 42` from the new test 
leaves all three new CHECK lines passing.

Could you account for device execution when deciding whether to initialize a 
local? Could you also make the test verify that the store targets the 
user-declared `n`?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

MattPD wrote:

The implementation stores the requested values in both fields of a record laid 
out as `{ i8, i32 }`, but it leaves the three padding bytes untouched.

Could you either fill every byte of the selected storage or state in the public 
contract that hex mode covers typed fields rather than every byte?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

MattPD wrote:

Static `CHARACTER(0)` initialization now works, but runtime-length `CHARACTER` 
initialization remains incorrect. For `character(n) :: x`, the generated LLVM 
IR still allocates `n` bytes and emits one unconditional byte store. When `n = 
0` at runtime, the store writes through a zero-byte allocation. Positive 
lengths initialize only one byte.

Could you use the runtime length from `ExtendedValue`? Could you also add a 
test with a runtime length of zero?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-16 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-16 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-16 Thread via cfe-commits


@@ -1250,6 +1254,267 @@ 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, vars with explicit or default initialization, and
+/// CUDA variables whose storage is not host-accessible (device, constant,
+/// shared, usedevice).
+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))

MattPD wrote:

Cray pointees pass the eligibility checks in `shouldInitLocal` even though 
their FIR base is a pointer-box descriptor and they own no storage. You can 
reproduce this by saving the following as `repro.f90`:

```fortran
integer :: res(10), x(10)
integer(8) :: p
pointer (p, x)
p = loc(res)
x(3) = 7
```

`flang -fc1 -emit-llvm -O0 -finit-local=zero repro.f90 -o -` emits a 48-byte 
`memcpy` from null into the pointee descriptor. With `-O2`, the function 
becomes `unreachable`. Compiling without `-finit-local`, or with an unpatched 
compiler, keeps the store to `x(3)`.

Could you exclude Cray pointees in `shouldInitLocal`? Could you reject FIR box 
types in the scalar fallback? Could you also add `-O0` and `-O2` LLVM 
regression tests?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

DanielCChen wrote:

> An isolated empty value is now rejected. The last-option-wins behavior 
> remains inconsistent when the overridden value is invalid. Both `bbc 
> -finit-local= -finit-local-zero` and `bbc -finit-local=bogus 
> -finit-local-zero` exit with status 1. The `flang -fc1` frontend accepts both 
> option sequences and selects zero.
> 
> Could `bbc` select the last occurrence before validating its value? Could you 
> also add tests for both listed option sequences?

Fixed by evaluating `getPosition()` before validation so that `-finit-local= 
-finit-local-zero` and `-finit-local=bogus -finit-local-zero` both select zero 
without failing on the overridden value; added regression tests for both 
sequences.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

DanielCChen wrote:

> Pinned and unified CUDA host storage is now initialized correctly. Managed 
> host storage is still skipped even though an explicit assignment to a managed 
> scalar lowers to `hlfir.assign`. Ordinary per-thread locals in an 
> `attributes(global)` procedure are also skipped after they are marked 
> `data_attr=device`, even though their storage uses `fir.alloca`.
> 
> Could initialization eligibility account for the CUDA execution context? 
> Could you also add two CUDA test cases: managed host storage and per-thread 
> locals in an `attributes(global)` procedure?

Removed Managed from the exclusion list so non-allocatable managed storage is 
initialized via a plain host store like unified and pinned; added test_managed 
and test_global_local test cases to cover both scenarios.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

> One array-of-records case remains incorrect. For `type(t) :: x(2)` with 
> `-finit-local=0xAA`, the code reaches `genInitLocalStore` for the record 
> element, but LLVM stores a `zeroinitializer`.
> 
> Could the array loop initialize each element through `initAddr`? Could you 
> also add an LLVM test for an array of records?

Fixed by calling `initAddr(eleTy, elemAddr)` instead of `genInitLocalStore` in 
the array loop so record elements recurse through the field-walk path; added 
LLVM regression tests for rank-1 and rank-2 arrays of records.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

> The LOGICAL representation is fixed. For `character(n) :: x`, CHARACTER 
> initialization emits an unconditional one-byte store. At `n=0`, that store 
> writes through a zero-byte allocation. At positive lengths, only one byte is 
> initialized.
> 
> If complete CHARACTER support is outside this PR's scope, could you guard the 
> zero-length case?

Fixed. No store for zero-length character type object.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread via cfe-commits


@@ -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
+
+! NAN-LABEL:  func.func @_QPtest_real16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref
+
+! SNAN-LABEL: func.func @_QPtest_real16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref
+
+! HEX-LABEL:  func.func @_QPtest_real16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  fir.store {{.*}} : !fir.ref
+
+! ---
+! 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>

MattPD wrote:

I confirmed that the checks now require the exact spelling `complex`.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

MattPD wrote:

The LOGICAL representation is fixed. For `character(n) :: x`, CHARACTER 
initialization emits an unconditional one-byte store. At `n=0`, that store 
writes through a zero-byte allocation. At positive lengths, only one byte is 
initialized.

If complete CHARACTER support is outside this PR's scope, could you guard the 
zero-length case?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

MattPD wrote:

An isolated empty value is now rejected. The last-option-wins behavior remains 
inconsistent when the overridden value is invalid. Both `bbc -finit-local= 
-finit-local-zero` and `bbc -finit-local=bogus -finit-local-zero` exit with 
status 1. The `flang -fc1` frontend accepts both option sequences and selects 
zero.

Could `bbc` select the last occurrence before validating its value? Could you 
also add tests for both listed option sequences?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

MattPD wrote:

One array-of-records case remains incorrect. For `type(t) :: x(2)` with 
`-finit-local=0xAA`, the code reaches `genInitLocalStore` for the record 
element, but LLVM stores a `zeroinitializer`.

Could the array loop initialize each element through `initAddr`? Could you also 
add an LLVM test for an array of records?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

MattPD wrote:

Pinned and unified CUDA host storage is now initialized correctly. Managed host 
storage is still skipped even though an explicit assignment to a managed scalar 
lowers to `hlfir.assign`. Ordinary per-thread locals in an `attributes(global)` 
procedure are also skipped after they are marked `data_attr=device`, even 
though their storage uses `fir.alloca`.

Could initialization eligibility account for the CUDA execution context? Could 
you also add two CUDA test cases: managed host storage and per-thread locals in 
an `attributes(global)` procedure?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread Daniel Chen via cfe-commits

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 
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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

For CHARACTER type, its byte-fill for both fixed- and runtime-length remains a 
known TODO. Properly covering all bytes requires a runtime memset over the full 
character length. PR #159788 is doing something similar. I would suggest to see 
how that PR goes and come back to this.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

DanielCChen wrote:

Yeah. Should allow pinned and unified storage initialization.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

DanielCChen wrote:

Right. 
Fixed. `bbc -finit-local=` (empty value) now returns a non-zero exit code with 
an error message, matching `flang -fc1` behavior. Aslo added a regression test.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

For logical type, 

- the `fir.convert` indeed normalizes it to `1`. Will fix it to store the raw 
integer. 
- Also add a test flang/test/Lower/finit-local-logical-llvm.f90

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-15 Thread Daniel Chen via cfe-commits


@@ -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
+
+! NAN-LABEL:  func.func @_QPtest_real16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref
+
+! SNAN-LABEL: func.func @_QPtest_real16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref
+
+! HEX-LABEL:  func.func @_QPtest_real16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  fir.store {{.*}} : !fir.ref
+
+! ---
+! 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>

DanielCChen wrote:

Yeah. Fixed.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits

DanielCChen wrote:

> The PR description does not reflect the latest commits in five areas: the 
> statement that LOGICAL falls back to zero, the `!fir.complex<16>` spelling, 
> the omitted CUDA exclusion, the description of arrays as broadly supported, 
> and the claim that all types, modes, and exclusions are tested.
> 
> The two follow-up commit subjects describe review responses rather than the 
> changes, and both commit bodies are empty.
> 
> Could you update the PR description and follow-up commit messages to match 
> the final implementation?

- PR description updated.
- Added change-list to all 3 follow-up commits.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-15 Thread Daniel Chen via cfe-commits

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 
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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-15 Thread Daniel Chen via cfe-commits

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 
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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-15 Thread Daniel Chen via cfe-commits

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 
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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-15 Thread Daniel Chen via cfe-commits

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 
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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-14 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-14 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-14 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

DanielCChen wrote:

You are right.
Will fix that 

- invalid values now return an error and exit non-zero instead of silently 
continuing; 

- -finit-local= and -finit-local-zero now use getPosition() so the last 
occurrence on the command line always wins.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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
+
+! NAN-LABEL:  func.func @_QPtest_real16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref
+
+! SNAN-LABEL: func.func @_QPtest_real16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref
+
+! HEX-LABEL:  func.func @_QPtest_real16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  fir.store {{.*}} : !fir.ref
+
+! ---
+! 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>

DanielCChen wrote:

You are right. Change to `complex`

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

DanielCChen wrote:

This is a good catch!

The fall-back-to-zeros is not intended for the logical type as its width is 
known at the compile time. It is an oversight in the implementation. Will fix 
it.

For CHARACTER type, it was intentionally left as a TODO because of the 
possibility of the runtime length. The current approach of using compile-time 
constant splat wouldn't work. 
PR https://github.com/llvm/llvm-project/pull/159788 actually is using memset, 
which should patch this hole after it lands.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits

https://github.com/tarunprabhu edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

DanielCChen wrote:

Good point! Will add a check to exclude it.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits

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

>From 67e0052be66abf218812bf3d32957e043d3618d8 Mon Sep 17 00:00:00 2001
From: Daniel Chen 
Date: Thu, 13 Aug 2026 15:30:12 -0400
Subject: [PATCH 1/2] [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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm frontend_optimize : BooleanFFlag<"frontend-optimize">, 
Group;
-defm init_local_zero : BooleanFFlag<"init-local-zero">, Group;
 defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, 
Group;
 defm max_identifier_length : BooleanFFlag<"max-identifier-length">, 
Group;
 defm module_private : BooleanFFlag<"module-private">, Group;
@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// 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,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Initialize local variables without explicit or default 
initialization. "
+   "Accepts: zero, nan, snan, or 0x.">;
+
+def finit_local_zero : Flag<["-"], "finit-local-zero">,
+  Group,
+  Visibility<[FC1Option, FlangOption]>,
+  HelpText<"Zero-initialize local variables without explicit or default 
initialization "
+   "(alias for -finit-local=zero, GFortran compatibility)">,
+  Alias, AliasArgs<["zero"]>;
+
 def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, 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,
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=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)
+  variables that have no explicit or default initialization. Accepted values
+  are `zero`, `nan`, `snan`, and `0x` (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 

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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -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 -

DanielCChen wrote:

Good point!

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -57,6 +57,11 @@ page](https://llvm.org/releases/).
 - Added `-gz` and `-gz=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)

DanielCChen wrote:

Good question! 
"local" means variables that have the scope of a subprogram.
"stack-allocated" is supposed to mean the consequence of how those "local" 
variables get their storage. 
However, I just realized that there are options to allow users to put large 
"local" variables on the heap rather than the stack. The implementation of this 
PR will handle the initialization of such variables as well. So 
"stack-allocated" is not accurate here. I will remove them.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// Initialize automatic (local, stack) variables that have no explicit or
+// default initialization.  -finit-local-zero is a GFortran compatibility alias
+// for -finit-local=zero.

tarunprabhu wrote:

I don't think these comments are needed. The help text already explains this.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-14 Thread Daniel Chen via cfe-commits


@@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", 
"init-global-zero",
   PosFlag,
   NegFlag>;
 
+// -finit-local=>
+// Initialize automatic (local, stack) variables that have no explicit or
+// default initialization.  -finit-local-zero is a GFortran compatibility alias
+// for -finit-local=zero.

DanielCChen wrote:

Good point. Will remove it.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits

https://github.com/tarunprabhu commented:

I mainly looked at the driver-related code

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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

tarunprabhu wrote:

nit: The prefix could just be named "ZERO". The "CHECK-" in the name is 
understood.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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

tarunprabhu wrote:

The `-o -` is not necessary in the driver tests. In general, it is better to 
have as few options in the invocation lines as possible to make it clear what 
is actually being tested.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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

tarunprabhu wrote:

I believe that we have been using `%flang_fc1` to test lowering instead of 
`bbc` unless the latter is explicitly needed. @eugeneepshteyn, is that correct? 

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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

tarunprabhu wrote:

Instead of using "ALIAS" here which just matches the same string as "ZERO", I 
would suggest using "ZERO". Just by looking at the run line, the expectation is 
a lot more clear.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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 -

tarunprabhu wrote:

I don't think these are necessary. We should be using `%flang_fc1` to test the 
lowering anyway. If the options are not recognized, those will fail, which is 
generally good enough.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -57,6 +57,11 @@ page](https://llvm.org/releases/).
 - Added `-gz` and `-gz=` flags to enable compression of DWARF debug
   sections. Supported formats are `zlib`, `zstd`, and `none`.
 
+- Added `-finit-local=` to initialize automatic (local, stack-allocated)

tarunprabhu wrote:

What is the difference between "local" and "stack-allocated" here?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Tarun Prabhu via cfe-commits


@@ -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"

tarunprabhu wrote:

nit: I would add some spaces for readability.

```suggestion
! CHECK-ZERO:  "-fc1"{{.*}} "-finit-local=zero"
```

Alternatively, you could consider splitting these into separate prefixes. The 
latter looks a bit cleaner to me.
```
FC1: "-fc1"
ZERO-SAME: "-finit-local-zero"
```

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits

DanielCChen wrote:

> > +1 to the engineering but I fundamentally disagree with having 
> > `-finit-local=` cover all types at once. Assigning some arbitrary value to 
> > integer and logical when `=snan` is specified doesn't make sense to me. I 
> > think starting with gfortran's options is the way to go - and if you want 
> > to add the ability to specify a hex value in addition, even better.
> 
> Thanks for the quick review!
> 
> I have no objection to start with only `zero` and `0x` to begin with and 
> leave `nan` and `snan` out as those two (`zero` and `hex`) are the most 
> common usage. The Gfortran's `-finit-local-zero` is already implemented in 
> this PR as an aliasing to `-finit-local=zero`. I think we should keep this 
> option format so it is easier to expand in the future.

@rscottmanley 
I generated a Type mapping table for `-finit-local=` to show how each type is 
implemented so that we can decide if we want to drop the support for `nan` and 
`snan` for this PR.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-14 Thread Daniel Chen via cfe-commits

https://github.com/DanielCChen edited 
https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-13 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-13 Thread via cfe-commits


@@ -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())
+if (obj->init())
+  return false;
+  if (Fortran::semantics::FindEquivalenceSet(sym))
+return false;
+  return true;

MattPD wrote:

`shouldInitLocal` accepts CUDA device locals. Compiling `real, device :: x` 
with `-fcuda -finit-local=zero` lowers to `cuf.alloc` followed by a plain 
`fir.store`. The corresponding compilation without `-finit-local=zero` emits no 
store. A host assignment to CUDA device storage uses `cuf.data_transfer`.

Could `shouldInitLocal` exclude CUDA data attributes unless initialization uses 
a CUF transfer or memset? `needsRepack` already excludes them.

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-13 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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(ty);
+  auto cplxTy = mlir::dyn_cast(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(cplxTy.getElementType());
+  mlir::Value nanPart =
+  genFPNaNInit(builder, loc, partFpTy, 

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

2026-08-13 Thread via cfe-commits


@@ -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(hexVal));
+  } else {
+llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n";

MattPD wrote:

`bbc -finit-local=bogus` prints `bbc: invalid -finit-local= value: bogus` but 
exits with status zero and emits IR. `bbc` also selects `0xAA` for 
`-finit-local=0xAA -finit-local-zero`. By contrast, `flang -fc1` honors the 
later alias and selects zero.

Could both entry points use the same strict parser so that the last occurrence 
takes precedence?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-13 Thread via cfe-commits


@@ -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())
+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(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(eleTy)) {
+return makeIntCst(intTy.getWidth());
+  }
+  // Complex: apply the byte pattern to each (real, imag) part.
+  if (auto cplxTy = mlir::dyn_cast(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);

MattPD wrote:

`0xAA` initialization silently falls back to zero for CHARACTER and LOGICAL. A 
runtime-length CHARACTER also lowers to an allocation of `n` bytes followed by 
a single store of one zero byte, even when `n` is zero.

The [`-finit-local=` 
proposal](https://discourse.llvm.org/t/rfc-add-finit-local-to-flang-for-initializing-automatic-variables/91545)
 promises byte replication across the full storage. Could local initialization 
emit a length-aware byte fill that also covers nested storage and padding bytes?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-13 Thread via cfe-commits


@@ -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
+
+! NAN-LABEL:  func.func @_QPtest_real16
+! NAN:  arith.constant {{.*}} : f128
+! NAN:  fir.store {{.*}} : !fir.ref
+
+! SNAN-LABEL: func.func @_QPtest_real16
+! SNAN: arith.constant {{.*}} : f128
+! SNAN: fir.store {{.*}} : !fir.ref
+
+! HEX-LABEL:  func.func @_QPtest_real16
+! HEX:  arith.constant -113427455640312821154458202477256070486 : i128
+! HEX:  arith.bitcast {{.*}} : i128 to f128
+! HEX:  fir.store {{.*}} : !fir.ref
+
+! ---
+! 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>

MattPD wrote:

The COMPLEX(16) checks expect `!fir.complex<16>`. `ConvertType.cpp` constructs 
the builtin type `complex`. `cabs_real16.f90` uses `complex` under 
the same test feature guard. Builders with f128 support should reach these 
checks and fail.

Could the COMPLEX(16) checks use `complex`?

https://github.com/llvm/llvm-project/pull/216164
___
cfe-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits


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

2026-08-13 Thread via cfe-commits

github-actions[bot] wrote:




:warning: C/C++ code formatter, clang-format found issues in your code. 
:warning:



You can test this locally with the following command:


``bash
git-clang-format --diff origin/main HEAD --extensions cpp,h -- 
clang/lib/Driver/ToolChains/Flang.cpp 
flang/include/flang/Lower/LoweringOptions.h 
flang/lib/Frontend/CompilerInvocation.cpp flang/lib/Lower/ConvertVariable.cpp 
flang/tools/bbc/bbc.cpp --diff_from_common_commit
``

:warning:
The reproduction instructions above might return results for more than one PR
in a stack if you are using a stacked PR workflow. You can limit the results by
changing `origin/main` to the base branch/commit you want to compare against.
:warning:





View the diff from clang-format here.


``diff
diff --git a/clang/lib/Driver/ToolChains/Flang.cpp 
b/clang/lib/Driver/ToolChains/Flang.cpp
index 2928b62f9..096c592be 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -360,8 +360,7 @@ void Flang::addCodegenOptions(const ArgList &Args,
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_finit_local_EQ,
-   options::OPT_frepack_arrays,
-   options::OPT_fno_repack_arrays,
+   options::OPT_frepack_arrays, options::OPT_fno_repack_arrays,
options::OPT_frepack_arrays_contiguity_EQ,
options::OPT_fstack_repack_arrays, options::OPT_fno_stack_repack_arrays,
options::OPT_ftime_report, options::OPT_ftime_report_EQ,
diff --git a/flang/include/flang/Lower/LoweringOptions.h 
b/flang/include/flang/Lower/LoweringOptions.h
index 14f38ea77..7f24c02c5 100644
--- a/flang/include/flang/Lower/LoweringOptions.h
+++ b/flang/include/flang/Lower/LoweringOptions.h
@@ -16,22 +16,21 @@
 #define FLANG_LOWER_LOWERINGOPTIONS_H
 
 #include "flang/Support/FPMaxminBehavior.h"
-#include 
 #include "flang/Support/MathOptionsBase.h"
+#include 
 
 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
+  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;
diff --git a/flang/lib/Frontend/CompilerInvocation.cpp 
b/flang/lib/Frontend/CompilerInvocation.cpp
index d21b8f55f..5380a51a1 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1762,24 +1762,19 @@ bool CompilerInvocation::createFromArgs(
   args.getLastArg(clang::options::OPT_finit_local_EQ)) {
 llvm::StringRef val = a->getValue();
 if (val == "zero") {
-  invoc.loweringOpts.setInitLocalMode(
-  Fortran::lower::InitLocalKind::Zero);
+  invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Zero);
 } else if (val == "nan") {
-  invoc.loweringOpts.setInitLocalMode(
-  Fortran::lower::InitLocalKind::QNaN);
+  invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::QNaN);
 } else if (val == "snan") {
-  invoc.loweringOpts.setInitLocalMode(
-  Fortran::lower::InitLocalKind::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(hexVal));
+
invoc.loweringOpts.setInitLocalMode(Fortran::lower::InitLocalKind::Hex);
+invoc.loweringOpts.setInitLocalPattern(static_cast(hexVal));
   }
 } else {
   diags.Report(clang::diag::err_drv_invalid_value)
diff --git a/flang/lib/Lower/ConvertVariable.cpp 
b/flang/lib/Lower/ConvertVariable.cpp
index 2ebd03dd8..03ebe82cb 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -11,7 +11,6 @@
 
//===--===//
 
 #include "flang/Lower/ConvertVariable.h"
-#include "flang/Lower/LoweringOptions.h"
 

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

2026-08-13 Thread Daniel Chen via cfe-commits

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

This patch implements the `-finit-local=` compiler option, which
initializes automatic (local, stack-allocated) Fortran variables that
have no explicit or default initialization.

RFC: 
https://discourse.llvm.org/t/rfc-add-finit-local-to-flang-for-initializing-automatic-variables/91545

## Accepted values

| Value| Effect |
|--||
| `zero`   | Fill with zero bits|
| `nan`| Fill with a quiet NaN (FP types) or 0xAA... (int) |
| `snan`   | Fill with a signaling NaN (FP types) or 0xAA... (int) |
| `0x` | Fill every byte with the given hex byte pattern|

The gfortran compatibility alias `-finit-local-zero` is equivalent to
`-finit-local=zero`.

## Excluded variables (not initialized)

- Variables with explicit initializers (`integer :: x = 42`)
- Variables initialized via DATA statement
- Derived-type variables whose type has default component initialization
- SAVE variables (static storage)
- Dummy arguments
- ALLOCATABLE and POINTER variables
- Variables in EQUIVALENCE sets

## Implementation

| File | Change |
|--||
| `clang/include/clang/Options/FlangOptions.td` | Adds `finit_local_EQ` joined 
option and `finit_local_zero` alias |
| `clang/lib/Driver/ToolChains/Flang.cpp` | Forwards the option to the frontend 
via `addAllArgs` |
| `flang/include/flang/Lower/LoweringOptions.h` | Adds `InitLocalKind` enum and 
`InitLocalMode`/`InitLocalPattern` fields |
| `flang/include/flang/Lower/LoweringOptions.def` | Registers `InitLocalMode` 
lowering option |
| `flang/lib/Frontend/CompilerInvocation.cpp` | Parses option values and 
populates lowering options |
| `flang/lib/Lower/ConvertVariable.cpp` | Core lowering: `genInitLocal()` 
called from `instantiateLocal()` for every auto variable (else-branch of 
`mustBeDefaultInitializedAtRuntime`). Zero emits `fir.zero_bits`. Hex splats 
the byte pattern via `APInt::getSplat` at the natural bit width of the element 
type (`fpTy.getWidth()` for FP, `intTy.getWidth()` for integers) and bitcasts 
to the FP type for real types; for COMPLEX the splat is applied to each part 
and combined with `complex.create` — REAL(16)/COMPLEX(16) work without 
special-casing via a natural i128→f128 bitcast. NaN/SNaN calls 
`APFloat::getQNaN`/`getSNaN` with all-ones mantissa payload and negative sign 
using `fpTy.getFloatSemantics()` (IEEEsingle/IEEEdouble/IEEEquad); integer 
types in nan/snan mode fall back to a hardcoded `0xAA` byte-splat. 
`fir.logical` and CHARACTER fall back to `fir.zero_bits` for all non-zero 
modes. Derived types use a single `fir.zero_bits` store for zero mode, and walk 
fields via `fir.coordinate_of` for all other modes. Arrays use 
`fir.insert_on_range`. |
| `flang/tools/bbc/bbc.cpp` | Adds matching `cl::opt` entries for standalone 
`bbc` testing |

## Tests

| File | Coverage |
|--|--|
| `flang/test/Driver/finit-local.f90` | Driver-level pass-through checks |
| `flang/test/Lower/finit-local.f90` | HLFIR lowering checks covering all types 
(INTEGER(1/2/4/8), REAL(4/8), COMPLEX(4/8), LOGICAL(1/4), CHARACTER, derived 
type, 1-D and 2-D arrays), all modes, and all exclusion cases |
| `flang/test/Lower/finit-local-f128.f90` | HLFIR lowering checks for REAL(16) 
and COMPLEX(16); requires `flang-supports-f128-math` |

>From 3d07ee2be553c71e7dd0591358f03220a97537ec Mon Sep 17 00:00:00 2001
From: Daniel Chen 
Date: Thu, 13 Aug 2026 15:30:12 -0400
Subject: [PATCH] [flang] Add -finit-local= to initialize automatic variables

---
 clang/include/clang/Options/FlangOptions.td   |  18 +-
 clang/lib/Driver/ToolChains/Flang.cpp |   3 +-
 flang/docs/ReleaseNotes.md|   5 +
 flang/include/flang/Lower/LoweringOptions.def |   6 +
 flang/include/flang/Lower/LoweringOptions.h   |  23 +
 flang/lib/Frontend/CompilerInvocation.cpp |  32 ++
 flang/lib/Lower/ConvertVariable.cpp   | 239 +
 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   |  38 ++
 11 files changed, 955 insertions(+), 2 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;
 defm external_blas : BooleanFFlag<"external-blas">, Group;
 defm f2c : BooleanFFlag<"f2c">, Group;
 defm