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

Static rank-two and rank-three arrays now address every element. Zero-mode 
initialization now scales with the array size.

The new LLVM tests do not prove that the loop covers every element. The `HEX` 
CHECK lines still pass against a hand-written input that has one correctly 
valued store per function, with no loop and no GEP.

Could you strengthen the tests by asserting the trip counts and the GEP strides 
for scalar and record arrays? Could you instead add an executable check of 
every element?

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

Reply via email to