[flang-commits] [clang] [flang] [flang] Add -finit-local= to initialize automatic variables (PR #216164)
via flang-commits
flang-commits at lists.llvm.org
Thu Aug 13 22:14:48 PDT 2026
================
@@ -1250,6 +1254,236 @@ getSafeRepackAttrs(Fortran::lower::AbstractConverter &converter) {
return attrs.empty() ? mlir::ArrayAttr{} : builder.getArrayAttr(attrs);
}
+//===----------------------------------------------------------------------===//
+// -finit-local= helpers
+//===----------------------------------------------------------------------===//
+
+/// Returns true when \p var is an automatic local variable eligible for
+/// -finit-local= initialization. Excluded: variables without a symbol,
+/// globals, dummy arguments, SAVE'd vars, ALLOCATABLE/POINTER, vars in
+/// an EQUIVALENCE set, and vars with explicit or default initialization.
+static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) {
+ if (!var.hasSymbol() || var.isGlobal())
+ return false;
+ const Fortran::semantics::Symbol &sym = var.getSymbol();
+ if (Fortran::semantics::IsDummy(sym))
+ return false;
+ if (Fortran::semantics::IsSaved(sym))
+ return false;
+ if (Fortran::semantics::IsAllocatableOrPointer(sym))
+ return false;
+ if (Fortran::lower::hasDefaultInitialization(sym))
+ return false;
+ if (const auto *obj =
+ sym.detailsIf<Fortran::semantics::ObjectEntityDetails>())
+ if (obj->init())
+ return false;
+ if (Fortran::semantics::FindEquivalenceSet(sym))
+ return false;
+ return true;
+}
+
+/// Build a constant whose every byte equals \p bytePat.
+/// FP types: bitcast from an integer splat. Complex: apply to both parts.
+/// Character: falls back to fir.zero_bits (see TODO). Derived types are
+/// handled by the caller before this function is reached.
+static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Type ty,
+ uint8_t bytePat) {
+ mlir::Type eleTy = fir::unwrapSequenceType(ty);
+
+ // Build an integer constant of the given bit width from a byte splat.
+ auto makeIntCst = [&](unsigned bits) -> mlir::Value {
+ llvm::APInt byteVal(8, bytePat);
+ llvm::APInt splat = llvm::APInt::getSplat(bits, byteVal);
+ mlir::Type intTy = builder.getIntegerType(bits);
+ return mlir::arith::ConstantOp::create(
+ builder, loc, intTy, builder.getIntegerAttr(intTy, splat));
+ };
+
+ if (auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy)) {
+ unsigned bits = fpTy.getWidth();
+ mlir::Value intCst = makeIntCst(bits);
+ return mlir::arith::BitcastOp::create(builder, loc, fpTy, intCst);
+ }
+ if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(eleTy)) {
+ return makeIntCst(intTy.getWidth());
+ }
+ // Complex: apply the byte pattern to each (real, imag) part.
+ if (auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy)) {
+ mlir::Type partTy = cplxTy.getElementType();
+ mlir::Value partVal = genByteSplatInit(builder, loc, partTy, bytePat);
+ return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal,
+ partVal);
+ }
+ // TODO: CHARACTER falls back to zero; a future improvement should fill each
+ // storage unit with the byte pattern.
+ return fir::ZeroOp::create(builder, loc, eleTy);
+}
+
+/// Build a quiet or signalling NaN constant of the given FP type.
+/// The payload is all-ones (matching clang's initializationPatternFor() and
+/// the RFC spec), and the sign bit is set (negative NaN).
+static mlir::Value genFPNaNInit(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::FloatType fpTy, bool isSignalling) {
+ const llvm::fltSemantics &sem = fpTy.getFloatSemantics();
+ // All-ones payload (precision-1 mantissa bits), negative sign, per RFC.
+ llvm::APInt payload = llvm::APInt::getAllOnes(sem.precision - 1);
+ llvm::APFloat apf =
+ isSignalling ? llvm::APFloat::getSNaN(sem, /*Negative=*/true, &payload)
+ : llvm::APFloat::getQNaN(sem, /*Negative=*/true, &payload);
+ return mlir::arith::ConstantFloatOp::create(builder, loc, fpTy, apf);
+}
+
+/// Emit a store of the -finit-local= pattern for a single scalar address.
+/// Complex types get NaN on both parts; other non-FP types use 0xAA byte-splat
+/// for nan/snan modes.
+static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Type ty, mlir::Value addr,
+ Fortran::lower::InitLocalKind mode,
+ uint8_t hexByte) {
+ mlir::Value val;
+ auto fpTy = mlir::dyn_cast<mlir::FloatType>(ty);
+ auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(ty);
+ switch (mode) {
+ case Fortran::lower::InitLocalKind::Zero:
+ val = fir::ZeroOp::create(builder, loc, ty);
+ break;
+ case Fortran::lower::InitLocalKind::Hex:
+ val = genByteSplatInit(builder, loc, ty, hexByte);
+ break;
+ case Fortran::lower::InitLocalKind::QNaN:
+ if (fpTy) {
+ val = genFPNaNInit(builder, loc, fpTy, /*signalling=*/false);
+ } else if (cplxTy) {
+ auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+ mlir::Value nanPart =
+ genFPNaNInit(builder, loc, partFpTy, /*signalling=*/false);
+ val = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+ nanPart);
+ } else {
+ val = genByteSplatInit(builder, loc, ty, 0xAA);
+ }
+ break;
+ case Fortran::lower::InitLocalKind::SNaN:
+ if (fpTy) {
+ val = genFPNaNInit(builder, loc, fpTy, /*signalling=*/true);
+ } else if (cplxTy) {
+ auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+ mlir::Value nanPart =
+ genFPNaNInit(builder, loc, partFpTy, /*signalling=*/true);
+ val = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+ nanPart);
+ } else {
+ val = genByteSplatInit(builder, loc, ty, 0xAA);
+ }
+ break;
+ default:
+ llvm_unreachable("unexpected InitLocalKind in genInitLocalStore");
+ }
+ fir::StoreOp::create(builder, loc, val, addr);
+}
+
+/// Initialize all storage of the local variable \p var per -finit-local= mode.
+/// Arrays use insert_on_range. Derived types walk fields for nan/snan/hex.
+/// Scalars store directly.
+static void genInitLocal(Fortran::lower::AbstractConverter &converter,
+ const Fortran::lower::pft::Variable &var,
+ Fortran::lower::SymMap &symMap) {
+ Fortran::lower::InitLocalKind mode =
+ converter.getLoweringOptions().getInitLocalMode();
+ if (mode == Fortran::lower::InitLocalKind::Off)
+ return;
+ if (!shouldInitLocal(var))
+ return;
+
+ fir::FirOpBuilder &builder = converter.getFirOpBuilder();
+ mlir::Location loc = converter.getCurrentLocation();
+ uint8_t hexByte = converter.getLoweringOptions().getInitLocalPattern();
+
+ fir::ExtendedValue exv =
+ converter.getSymbolExtendedValue(var.getSymbol(), &symMap);
+ mlir::Value base = fir::getBase(exv);
+ mlir::Type storeTy = fir::unwrapRefType(base.getType());
+
+ if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(storeTy)) {
+ // Array: build element constant and use insert_on_range.
+ mlir::Type eleTy = seqTy.getEleTy();
+ auto fpTy = mlir::dyn_cast<mlir::FloatType>(eleTy);
+ auto cplxTy = mlir::dyn_cast<mlir::ComplexType>(eleTy);
+ mlir::Value elePat;
+ switch (mode) {
+ case Fortran::lower::InitLocalKind::Zero:
+ elePat = fir::ZeroOp::create(builder, loc, eleTy);
+ break;
+ case Fortran::lower::InitLocalKind::Hex:
+ elePat = genByteSplatInit(builder, loc, eleTy, hexByte);
+ break;
+ case Fortran::lower::InitLocalKind::QNaN:
+ if (fpTy)
+ elePat = genFPNaNInit(builder, loc, fpTy, false);
+ else if (cplxTy) {
+ auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+ mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, false);
+ elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+ nanPart);
+ } else
+ elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+ break;
+ case Fortran::lower::InitLocalKind::SNaN:
+ if (fpTy)
+ elePat = genFPNaNInit(builder, loc, fpTy, true);
+ else if (cplxTy) {
+ auto partFpTy = mlir::cast<mlir::FloatType>(cplxTy.getElementType());
+ mlir::Value nanPart = genFPNaNInit(builder, loc, partFpTy, true);
+ elePat = mlir::complex::CreateOp::create(builder, loc, cplxTy, nanPart,
+ nanPart);
+ } else
+ elePat = genByteSplatInit(builder, loc, eleTy, 0xAA);
+ break;
+ default:
+ llvm_unreachable("unexpected InitLocalKind");
+ }
+ // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector.
+ llvm::SmallVector<int64_t> rangeBounds;
+ bool hasUnknown = false;
+ for (auto dim : seqTy.getShape()) {
+ if (dim == fir::SequenceType::getUnknownExtent()) {
+ hasUnknown = true;
+ break;
+ }
+ rangeBounds.push_back(0);
+ rangeBounds.push_back(dim - 1);
+ }
+ if (!hasUnknown) {
+ mlir::Value arrVal = fir::UndefOp::create(builder, loc, seqTy);
+ arrVal =
+ fir::InsertOnRangeOp::create(builder, loc, seqTy, arrVal, elePat,
----------------
MattPD wrote:
The aggregate initialization path accepts array declarations that it does not handle. `flang -fc1 -emit-llvm -finit-local=zero` aborts in `InsertOnRangeOpConversion` for `character(4) :: x(3)`. `integer :: x(0)` fails FIR verification. `integer :: x(n)` receives no initialization.
Could aggregate initialization broadcast a scalar with `hlfir.assign` or use another fill that handles runtime shapes? Could you add an LLVM IR regression test to cover these declarations?
https://github.com/llvm/llvm-project/pull/216164
More information about the flang-commits
mailing list