[clang] [flang] [flang] Add -finit-local= to initialize automatic variables (PR #216164)
@@ -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)
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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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)
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)
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)
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)
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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
@@ -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)
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)
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
