[flang-commits] [clang] [flang] [llvm] [flang] Add -finit-local= to initialize automatic variables (PR #216164)
via flang-commits
flang-commits at lists.llvm.org
Thu Sep 3 19:18:02 PDT 2026
================
@@ -1250,6 +1253,465 @@ getSafeRepackAttrs(Fortran::lower::AbstractConverter &converter) {
return attrs.empty() ? mlir::ArrayAttr{} : builder.getArrayAttr(attrs);
}
+//===----------------------------------------------------------------------===//
+// -finit-local= helpers
+//===----------------------------------------------------------------------===//
+
+/// Returns true if \p derived or any of its components (recursively) is a
+/// PowerPC vector type. fir::VectorType does not implement
+/// DataLayoutTypeInterface, so any record containing a vector component would
+/// crash record-size calculation. Excluding such records at eligibility time
+/// avoids the crash.
+static bool containsVectorComponent(
+ const Fortran::semantics::DerivedTypeSpec &derived) {
+ if (derived.IsVectorType())
+ return true;
+ const Fortran::semantics::Scope *scope = derived.GetScope();
+ if (!scope)
+ return false;
+ const Fortran::semantics::Symbol &typeSym = derived.typeSymbol();
+ const auto *details =
+ typeSym.detailsIf<Fortran::semantics::DerivedTypeDetails>();
+ if (!details)
+ return false;
+ for (const Fortran::semantics::SourceName &compName :
+ details->componentNames()) {
+ auto it = scope->find(compName);
+ if (it == scope->cend())
+ continue;
+ const Fortran::semantics::Symbol &comp = it->second.get();
+ if (const Fortran::semantics::DeclTypeSpec *compTy = comp.GetType())
+ if (const Fortran::semantics::DerivedTypeSpec *compDerived =
+ compTy->AsDerived())
+ if (containsVectorComponent(*compDerived))
+ return true;
+ }
+ return false;
+}
+
+/// 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 always unreachable by a plain fir.store
+/// (constant, shared, usedevice). The Device case is deferred to genInitLocal
+/// which applies cuf::isCUDADeviceContext to distinguish cuf.alloc from
+/// fir.alloca storage.
+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;
+ // Cray pointees own no storage of their own; their FIR base is a
+ // pointer-box descriptor. Initializing it would overwrite the
+ // descriptor, not the pointee storage.
+ if (sym.test(Fortran::semantics::Symbol::Flag::CrayPointee))
+ return false;
+ // PowerPC vector types (vector(real(4)) etc.) lower to fir::VectorType
+ // which does not implement DataLayoutTypeInterface at the HLFIR level.
+ // Without this guard, a direct vector local would silently fall back to
+ // zero initialization regardless of the requested mode, and a derived-type
+ // local whose component is a vector type would crash record-size
+ // calculation. Exclude both cases here by walking components recursively.
+ if (const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType())
+ if (const Fortran::semantics::DerivedTypeSpec *derived =
+ declTy->AsDerived())
+ if (containsVectorComponent(*derived))
+ return false;
+ // CUDA storage accessibility:
+ // constant / shared / usedevice: always unreachable by a plain fir.store
+ // from the host -- skip.
+ // device: the allocation choice (cuf.alloc vs fir.alloca) depends on
+ // whether the insertion point is in a device context; that check
+ // requires the MLIR builder and is deferred to genInitLocal, which
+ // calls cuf::isCUDADeviceContext(builder.getRegion()) after this
+ // predicate returns true.
+ // managed / unified / pinned: host-accessible unified memory -- initialize.
+ if (auto cudaAttr = Fortran::semantics::GetCUDADataAttr(&sym)) {
+ switch (*cudaAttr) {
+ case Fortran::common::CUDADataAttr::Constant:
+ case Fortran::common::CUDADataAttr::Shared:
+ case Fortran::common::CUDADataAttr::UseDevice:
+ return false;
+ default:
+ break;
+ }
+ }
+ return true;
+}
+
+/// Build a constant whose every byte equals \p bytePat.
+/// Handles: integer, float (bitcast from integer splat), complex (both parts),
+/// and logical (raw integer, stored via bitcasted address by the caller).
+/// Character, derived-type, and sequence types are all intercepted by
+/// genInitLocalStore or initAddr before this function is called and must
+/// not reach it. fir::VectorType (PowerPC vector types, direct or as a
+/// derived-type component) is excluded upstream by shouldInitLocal via
+/// containsVectorComponent and will never reach this function.
+static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder,
+ mlir::Location loc, mlir::Type ty,
+ uint8_t bytePat) {
+ mlir::Type eleTy = fir::unwrapSequenceType(ty);
+
+ // Build a signless integer constant from a byte splat. arith.constant
+ // requires a signless integer type; callers that need a non-signless result
+ // (e.g. unsigned ui32) must fir.convert the returned value themselves.
+ 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)) {
+ mlir::Value intCst = makeIntCst(fpTy.getWidth());
+ return mlir::arith::BitcastOp::create(builder, loc, fpTy, intCst);
+ }
+ if (auto intTy = mlir::dyn_cast<mlir::IntegerType>(eleTy)) {
+ mlir::Value cst = makeIntCst(intTy.getWidth());
+ // arith.constant only supports signless integers; fir.convert reinterprets
+ // the bit pattern into the declared signed or unsigned type without
+ // changing any bits, satisfying FIR verification for !fir.ref<ui32> etc.
+ if (!intTy.isSignless())
+ cst = builder.createConvert(loc, intTy, cst);
+ return cst;
+ }
+ // 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);
+ }
+ // LOGICAL(k) has a fixed size of k bytes. Return the raw integer splat;
+ // the caller stores it via a bitcasted address to preserve the bit pattern
+ // (fir.convert from integer to !fir.logical normalizes nonzero -> true).
+ if (auto logTy = mlir::dyn_cast<fir::LogicalType>(eleTy)) {
+ return makeIntCst(logTy.getFKind() * 8);
+ }
+ // All types that pass shouldInitLocal and reach genInitLocalStore are
+ // handled explicitly above (integer, float, complex, logical) or are
+ // intercepted before this call (character, record, sequence).
+ // PowerPC vector types (direct or as a derived-type component) are excluded
+ // by shouldInitLocal via containsVectorComponent and never reach here.
+ // A silent zero for an unhandled type would violate the hex-mode contract,
+ // so assert rather than fall back silently.
+ llvm_unreachable("genByteSplatInit: unhandled type in hex mode");
+}
+
+/// Emit a store of the -finit-local= pattern for a single scalar address.
+/// Fixed-length CHARACTER in hex mode: byte-loop over every byte of storage.
+/// LOGICAL stores via a bitcasted integer address to preserve the raw bit
+/// pattern past fir.convert normalization.
+static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc,
+ mlir::Type ty, mlir::Value addr,
+ Fortran::lower::InitLocalKind mode,
+ uint8_t hexByte) {
+ // Fixed-length CHARACTER: for hex mode emit a compile-time byte-loop so
+ // every code-unit gets the requested pattern. Zero falls through to
+ // fir.zero_bits below.
+ if (auto charTy = mlir::dyn_cast<fir::CharacterType>(ty)) {
+ // CHARACTER(0) has zero-length storage -- nothing to initialize.
+ if (charTy.getLen() == 0)
+ return;
+ if (mode == Fortran::lower::InitLocalKind::Hex) {
+ // Loop over every byte of the character storage. For kind=1 each
+ // code unit is one byte; for kind=2/4 (UTF-16/32) each code unit is
+ // kind bytes wide. We use a kind=1 singleton as the view element so
+ // fir.coordinate_of advances exactly one byte per step, and iterate
+ // nUnits * kind times to cover all bytes.
+ int64_t nUnits = charTy.hasConstantLen() ? charTy.getLen() : 0;
+ int64_t kindBytes = charTy.getFKind();
+ int64_t nBytes = nUnits * kindBytes;
+ if (nBytes > 0) {
+ mlir::Type idxTy = builder.getIndexType();
+ mlir::Type i8Ty = builder.getIntegerType(8);
+ // Use a kind=1 singleton so fir.coordinate_of strides by 1 byte.
+ fir::CharacterType byteTy =
+ fir::CharacterType::getSingleton(builder.getContext(), 1);
+ mlir::Type byteSeqTy = fir::SequenceType::get(
+ {fir::SequenceType::getUnknownExtent()}, byteTy);
+ mlir::Value byteBase =
+ builder.createConvertWithVolatileCast(
----------------
MattPD wrote:
Confirmed, including both CHARACTER paths.
https://github.com/llvm/llvm-project/pull/216164
More information about the flang-commits
mailing list