https://github.com/DanielCChen created https://github.com/llvm/llvm-project/pull/216164
This patch implements the `-finit-local=<val>` 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<hh>` | 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 <[email protected]> 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<gfortran_Group>; defm external_blas : BooleanFFlag<"external-blas">, Group<gfortran_Group>; defm f2c : BooleanFFlag<"f2c">, Group<gfortran_Group>; defm frontend_optimize : BooleanFFlag<"frontend-optimize">, Group<gfortran_Group>; -defm init_local_zero : BooleanFFlag<"init-local-zero">, Group<gfortran_Group>; defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, Group<gfortran_Group>; defm max_identifier_length : BooleanFFlag<"max-identifier-length">, Group<gfortran_Group>; defm module_private : BooleanFFlag<"module-private">, Group<gfortran_Group>; @@ -394,6 +393,23 @@ defm init_global_zero : BoolOptionWithoutMarshalling<"f", "init-global-zero", PosFlag<SetTrue, [], [], "Zero initialize globals without default initialization (default)">, NegFlag<SetFalse, [], [], "Do not zero initialize globals without default initialization">>; +// -finit-local=<zero|nan|snan|0x<hex>> +// 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<f_Group>, + Visibility<[FC1Option, FlangOption]>, + HelpText<"Initialize local variables without explicit or default initialization. " + "Accepts: zero, nan, snan, or 0x<hex-byte>.">; + +def finit_local_zero : Flag<["-"], "finit-local-zero">, + Group<f_Group>, + Visibility<[FC1Option, FlangOption]>, + HelpText<"Zero-initialize local variables without explicit or default initialization " + "(alias for -finit-local=zero, GFortran compatibility)">, + Alias<finit_local_EQ>, AliasArgs<["zero"]>; + def fno_realloc_lhs : Flag<["-"], "fno-realloc-lhs">, Group<f_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<f_Group>, diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index a48e41159f367..2928b62f99d20 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -359,7 +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_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, 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=<format>` flags to enable compression of DWARF debug sections. Supported formats are `zlib`, `zstd`, and `none`. +- Added `-finit-local=<val>` to initialize automatic (local, stack-allocated) + variables that have no explicit or default initialization. Accepted values + are `zero`, `nan`, `snan`, and `0x<hex-byte>` (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 @@ ENUM_LOWERINGOPT(FPMaxminBehavior, Fortran::common::FPMaxminBehavior, 2, 0) /// 0 means no trapping. Bit values match IEEE_FLAG_TYPE encoding. ENUM_LOWERINGOPT(FPExceptionTraps, unsigned, 8, 0) +/// Initialization mode for automatic variables that have no explicit or +/// default initialization (-finit-local= / -finit-local-zero). +/// Off by default. +ENUM_LOWERINGOPT(InitLocalMode, Fortran::lower::InitLocalKind, 3, + Fortran::lower::InitLocalKind::Off) + #undef LOWERINGOPT #undef ENUM_LOWERINGOPT diff --git a/flang/include/flang/Lower/LoweringOptions.h b/flang/include/flang/Lower/LoweringOptions.h index d44d5f73eeb67..14f38ea77b34a 100644 --- a/flang/include/flang/Lower/LoweringOptions.h +++ b/flang/include/flang/Lower/LoweringOptions.h @@ -16,10 +16,22 @@ #define FLANG_LOWER_LOWERINGOPTIONS_H #include "flang/Support/FPMaxminBehavior.h" +#include <cstdint> #include "flang/Support/MathOptionsBase.h" 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 +}; + + class LoweringOptionsBase { public: #define LOWERINGOPT(Name, Bits, Default) unsigned Name : Bits; @@ -52,7 +64,18 @@ class LoweringOptions : public LoweringOptionsBase { Fortran::common::MathOptionsBase &getMathOptions() { return MathOptions; } + /// Returns the byte pattern used for -finit-local=0x<hex>. + uint8_t getInitLocalPattern() const { return InitLocalPattern; } + LoweringOptions &setInitLocalPattern(uint8_t V) { + InitLocalPattern = V; + return *this; + } + private: + /// Byte pattern for -finit-local=0x<hex>. Only meaningful when + /// getInitLocalMode() == InitLocalKind::Hex. + uint8_t InitLocalPattern = 0; + /// Options for handling/optimizing mathematical computations. Fortran::common::MathOptionsBase MathOptions; }; diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index b57bc4583be38..d21b8f55fef74 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -1755,6 +1755,38 @@ bool CompilerInvocation::createFromArgs( else invoc.loweringOpts.setInitGlobalZero(false); + // -finit-local=<zero|nan|snan|0x<hex>> and -finit-local-zero + // (-finit-local-zero is an alias that the driver already expands to + // -finit-local=zero, so we only need to handle OPT_finit_local_EQ here.) + if (const llvm::opt::Arg *a = + args.getLastArg(clang::options::OPT_finit_local_EQ)) { + llvm::StringRef val = a->getValue(); + if (val == "zero") { + invoc.loweringOpts.setInitLocalMode( + Fortran::lower::InitLocalKind::Zero); + } else if (val == "nan") { + invoc.loweringOpts.setInitLocalMode( + Fortran::lower::InitLocalKind::QNaN); + } else if (val == "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<uint8_t>(hexVal)); + } + } else { + diags.Report(clang::diag::err_drv_invalid_value) + << a->getAsString(args) << val; + } + } + // Preserve all the remark options requested, i.e. -Rpass, -Rpass-missed or // -Rpass-analysis. This will be used later when processing and outputting the // remarks generated by LLVM in ExecuteCompilerInvocation.cpp. diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index a808905850922..2ebd03dd86149 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -11,6 +11,7 @@ //===----------------------------------------------------------------------===// #include "flang/Lower/ConvertVariable.h" +#include "flang/Lower/LoweringOptions.h" #include "flang/Lower/AbstractConverter.h" #include "flang/Lower/Allocatable.h" #include "flang/Lower/BoxAnalyzer.h" @@ -47,6 +48,9 @@ #include "flang/Semantics/type.h" #include "mlir/Dialect/OpenACC/OpenACC.h" #include "llvm/ADT/SmallVector.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" +#include "mlir/Dialect/Complex/IR/Complex.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" #include <optional> @@ -1250,6 +1254,239 @@ 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, + builder.getIndexVectorAttr(rangeBounds)); + fir::StoreOp::create(builder, loc, arrVal, base); + } + } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(storeTy)) { + // Derived type: zero the whole struct, or walk fields for nan/snan/hex. + if (mode == Fortran::lower::InitLocalKind::Zero) { + fir::StoreOp::create(builder, loc, + fir::ZeroOp::create(builder, loc, recTy), base); + } else { + for (auto [fieldName, fieldTy] : recTy.getTypeList()) { + auto fieldIdx = fir::FieldIndexOp::create( + builder, loc, fir::FieldType::get(recTy.getContext()), fieldName, + recTy, mlir::ValueRange{}); + mlir::Value fieldAddr = fir::CoordinateOp::create( + builder, loc, builder.getRefType(fieldTy), base, + mlir::ValueRange{fieldIdx}); + genInitLocalStore(builder, loc, fieldTy, fieldAddr, mode, hexByte); + } + } + } else { + // Scalar (integer, real, complex, logical, character): store directly. + genInitLocalStore(builder, loc, storeTy, base, mode, hexByte); + } +} + /// Instantiate a local variable. Precondition: Each variable will be visited /// such that if its properties depend on other variables, the variables upon /// which its properties depend will already have been visited. @@ -1273,6 +1510,8 @@ static void instantiateLocal(Fortran::lower::AbstractConverter &converter, if (mustBeDefaultInitializedAtRuntime(var)) Fortran::lower::defaultInitializeAtRuntime(converter, var.getSymbol(), symMap); + else + genInitLocal(converter, var, symMap); auto *builder = &converter.getFirOpBuilder(); bool needsHostCudaCleanup = needCUDAAlloc(var.getSymbol()) && !cuf::isCUDADeviceContext(builder->getRegion()); diff --git a/flang/test/Driver/finit-local.f90 b/flang/test/Driver/finit-local.f90 new file mode 100644 index 0000000000000..01f288b7a8803 --- /dev/null +++ b/flang/test/Driver/finit-local.f90 @@ -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" +! CHECK-NAN: "-fc1"{{.*}}"-finit-local=nan" +! CHECK-SNAN: "-fc1"{{.*}}"-finit-local=snan" +! CHECK-HEX: "-fc1"{{.*}}"-finit-local=0xAA" +! CHECK-HEX2: "-fc1"{{.*}}"-finit-local=0xff" +! CHECK-ALIAS: "-fc1"{{.*}}"-finit-local=zero" +! CHECK-ERR: error: invalid value 'bogus' in '-finit-local=bogus' + +subroutine dummy_sub() +end subroutine diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90 new file mode 100644 index 0000000000000..a59322c6df139 --- /dev/null +++ b/flang/test/Lower/finit-local-f128.f90 @@ -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<f128> + +! NAN-LABEL: func.func @_QPtest_real16 +! NAN: arith.constant {{.*}} : f128 +! NAN: fir.store {{.*}} : !fir.ref<f128> + +! SNAN-LABEL: func.func @_QPtest_real16 +! SNAN: arith.constant {{.*}} : f128 +! SNAN: fir.store {{.*}} : !fir.ref<f128> + +! HEX-LABEL: func.func @_QPtest_real16 +! HEX: arith.constant -113427455640312821154458202477256070486 : i128 +! HEX: arith.bitcast {{.*}} : i128 to f128 +! HEX: fir.store {{.*}} : !fir.ref<f128> + +! --------------------------------------------------------------------------- +! 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> +! ZERO: fir.store {{.*}} : !fir.ref<!fir.complex<16>> + +! NAN-LABEL: func.func @_QPtest_complex16 +! NAN: arith.constant {{.*}} : f128 +! NAN: complex.create {{.*}}, {{.*}} : f128 +! NAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>> + +! SNAN-LABEL: func.func @_QPtest_complex16 +! SNAN: arith.constant {{.*}} : f128 +! SNAN: complex.create {{.*}}, {{.*}} : f128 +! SNAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>> + +! HEX-LABEL: func.func @_QPtest_complex16 +! HEX: arith.constant -113427455640312821154458202477256070486 : i128 +! HEX: arith.bitcast {{.*}} : i128 to f128 +! HEX: complex.create {{.*}}, {{.*}} : f128 +! HEX: fir.store {{.*}} : !fir.ref<!fir.complex<16>> diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90 new file mode 100644 index 0000000000000..379a7842dd0ac --- /dev/null +++ b/flang/test/Lower/finit-local.f90 @@ -0,0 +1,496 @@ +! Tests for -finit-local= local variable initialization. +! +! Covers every Fortran type listed in the RFC type-mapping table: +! INTEGER(k) k=1,2,4,8 +! REAL(k) k=4,8 (k=16 in finit-local-f128.f90, requires flang-supports-f128-math) +! COMPLEX(k) k=4,8 (k=16 in finit-local-f128.f90, requires flang-supports-f128-math) +! LOGICAL(k) k=1,4 +! CHARACTER(n) +! Derived type (struct with plain-int and real components) +! Arrays of integer and real +! +! Modes exercised: zero, nan, snan, 0xAA (hex), and off (no flag). +! +! 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 +! RUN: bbc -emit-hlfir -o - %s | FileCheck --check-prefix=OFF %s +! RUN: bbc -emit-hlfir -finit-local-zero -o - %s | FileCheck --check-prefix=ZERO %s + +! --------------------------------------------------------------------------- +! INTEGER(1) -- 1-byte: pattern 0xAA = -86 (signed) = 170 (unsigned) +! --------------------------------------------------------------------------- +subroutine test_int1(res) + integer(1) :: res + integer(1) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int1 +! ZERO: fir.alloca i8 +! ZERO: fir.zero_bits i8 +! ZERO: fir.store {{.*}} : !fir.ref<i8> + +! NAN-LABEL: func.func @_QPtest_int1 +! NAN: arith.constant -86 : i8 +! NAN: fir.store {{.*}} : !fir.ref<i8> + +! HEX-LABEL: func.func @_QPtest_int1 +! HEX: arith.constant -86 : i8 +! HEX: fir.store {{.*}} : !fir.ref<i8> + +! OFF-LABEL: func.func @_QPtest_int1 +! OFF-NOT: fir.store {{.*}} : !fir.ref<i8> + +! --------------------------------------------------------------------------- +! INTEGER(2) -- 2-byte: pattern 0xAAAA = -21846 (signed) +! --------------------------------------------------------------------------- +subroutine test_int2(res) + integer(2) :: res + integer(2) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int2 +! ZERO: fir.zero_bits i16 + +! NAN-LABEL: func.func @_QPtest_int2 +! NAN: arith.constant -21846 : i16 +! NAN: fir.store {{.*}} : !fir.ref<i16> + +! HEX-LABEL: func.func @_QPtest_int2 +! HEX: arith.constant -21846 : i16 +! HEX: fir.store {{.*}} : !fir.ref<i16> + +! --------------------------------------------------------------------------- +! INTEGER(4) -- 4-byte: pattern 0xAAAAAAAA = -1431655766 (signed) +! --------------------------------------------------------------------------- +subroutine test_int4(res) + integer(4) :: res + integer(4) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int4 +! ZERO: fir.zero_bits i32 + +! NAN-LABEL: func.func @_QPtest_int4 +! NAN: arith.constant -1431655766 : i32 +! NAN: fir.store {{.*}} : !fir.ref<i32> + +! SNAN-LABEL: func.func @_QPtest_int4 +! SNAN: arith.constant -1431655766 : i32 +! SNAN: fir.store {{.*}} : !fir.ref<i32> + +! HEX-LABEL: func.func @_QPtest_int4 +! HEX: arith.constant -1431655766 : i32 +! HEX: fir.store {{.*}} : !fir.ref<i32> + +! OFF-LABEL: func.func @_QPtest_int4 +! OFF-NOT: fir.zero_bits + +! --------------------------------------------------------------------------- +! INTEGER(8) -- 8-byte: pattern 0xAAAAAAAAAAAAAAAA = -6148914691236517206 (signed) +! --------------------------------------------------------------------------- +subroutine test_int8(res) + integer(8) :: res + integer(8) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int8 +! ZERO: fir.zero_bits i64 + +! NAN-LABEL: func.func @_QPtest_int8 +! NAN: arith.constant -6148914691236517206 : i64 +! NAN: fir.store {{.*}} : !fir.ref<i64> + +! HEX-LABEL: func.func @_QPtest_int8 +! HEX: arith.constant -6148914691236517206 : i64 +! HEX: fir.store {{.*}} : !fir.ref<i64> + +! --------------------------------------------------------------------------- +! REAL(4) -- zero fills with fir.zero_bits; nan/snan with FP constant; hex bitcast +! --------------------------------------------------------------------------- +subroutine test_real4(res) + real(4) :: res + real(4) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_real4 +! ZERO: fir.zero_bits f32 +! ZERO: fir.store {{.*}} : !fir.ref<f32> + +! NAN-LABEL: func.func @_QPtest_real4 +! NAN: arith.constant {{.*}} : f32 +! NAN: fir.store {{.*}} : !fir.ref<f32> + +! SNAN-LABEL: func.func @_QPtest_real4 +! SNAN: arith.constant {{.*}} : f32 +! SNAN: fir.store {{.*}} : !fir.ref<f32> + +! HEX-LABEL: func.func @_QPtest_real4 +! HEX: arith.constant -1431655766 : i32 +! HEX: arith.bitcast {{.*}} : i32 to f32 +! HEX: fir.store {{.*}} : !fir.ref<f32> + +! OFF-LABEL: func.func @_QPtest_real4 +! OFF-NOT: fir.zero_bits + +! --------------------------------------------------------------------------- +! REAL(8) -- 8-byte FP +! --------------------------------------------------------------------------- +subroutine test_real8(res) + real(8) :: res + real(8) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_real8 +! ZERO: fir.zero_bits f64 +! ZERO: fir.store {{.*}} : !fir.ref<f64> + +! NAN-LABEL: func.func @_QPtest_real8 +! NAN: arith.constant {{.*}} : f64 +! NAN: fir.store {{.*}} : !fir.ref<f64> + +! SNAN-LABEL: func.func @_QPtest_real8 +! SNAN: arith.constant {{.*}} : f64 +! SNAN: fir.store {{.*}} : !fir.ref<f64> + +! HEX-LABEL: func.func @_QPtest_real8 +! HEX: arith.constant -6148914691236517206 : i64 +! HEX: arith.bitcast {{.*}} : i64 to f64 +! HEX: fir.store {{.*}} : !fir.ref<f64> + +! --------------------------------------------------------------------------- +! COMPLEX(4) -- two f32 parts; stored as complex<f32> +! nan/snan: both parts get NaN; hex: both parts get bitcast pattern +! --------------------------------------------------------------------------- +subroutine test_complex4(res) + complex(4) :: res + complex(4) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_complex4 +! ZERO: fir.zero_bits complex<f32> +! ZERO: fir.store {{.*}} : !fir.ref<complex<f32>> + +! NAN-LABEL: func.func @_QPtest_complex4 +! NAN: arith.constant {{.*}} : f32 +! NAN: complex.create {{.*}} : complex<f32> +! NAN: fir.store {{.*}} : !fir.ref<complex<f32>> + +! SNAN-LABEL: func.func @_QPtest_complex4 +! SNAN: arith.constant {{.*}} : f32 +! SNAN: complex.create {{.*}} : complex<f32> +! SNAN: fir.store {{.*}} : !fir.ref<complex<f32>> + +! HEX-LABEL: func.func @_QPtest_complex4 +! HEX: arith.constant -1431655766 : i32 +! HEX: arith.bitcast {{.*}} : i32 to f32 +! HEX: complex.create {{.*}} : complex<f32> +! HEX: fir.store {{.*}} : !fir.ref<complex<f32>> + +! --------------------------------------------------------------------------- +! COMPLEX(8) -- two f64 parts; stored as complex<f64> +! --------------------------------------------------------------------------- +subroutine test_complex8(res) + complex(8) :: res + complex(8) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_complex8 +! ZERO: fir.zero_bits complex<f64> +! ZERO: fir.store {{.*}} : !fir.ref<complex<f64>> + +! NAN-LABEL: func.func @_QPtest_complex8 +! NAN: arith.constant {{.*}} : f64 +! NAN: complex.create {{.*}} : complex<f64> +! NAN: fir.store {{.*}} : !fir.ref<complex<f64>> + +! SNAN-LABEL: func.func @_QPtest_complex8 +! SNAN: arith.constant {{.*}} : f64 +! SNAN: complex.create {{.*}} : complex<f64> +! SNAN: fir.store {{.*}} : !fir.ref<complex<f64>> + +! HEX-LABEL: func.func @_QPtest_complex8 +! HEX: arith.constant -6148914691236517206 : i64 +! HEX: arith.bitcast {{.*}} : i64 to f64 +! HEX: complex.create {{.*}} : complex<f64> +! HEX: fir.store {{.*}} : !fir.ref<complex<f64>> + +! --------------------------------------------------------------------------- +! LOGICAL(1) -- stored as i8; pattern 0xAA = -86 +! --------------------------------------------------------------------------- +subroutine test_logical1(res) + logical(1) :: res + logical(1) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_logical1 +! ZERO: fir.zero_bits !fir.logical<1> + +! NAN-LABEL: func.func @_QPtest_logical1 +! NAN: fir.zero_bits !fir.logical<1> +! NAN: fir.store {{.*}} : !fir.ref<!fir.logical<1>> + +! HEX-LABEL: func.func @_QPtest_logical1 +! HEX: fir.zero_bits !fir.logical<1> +! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<1>> + +! --------------------------------------------------------------------------- +! LOGICAL(4) -- stored as i32; pattern 0xAAAAAAAA = -1431655766 +! --------------------------------------------------------------------------- +subroutine test_logical4(res) + logical(4) :: res + logical(4) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_logical4 +! ZERO: fir.zero_bits !fir.logical<4> + +! NAN-LABEL: func.func @_QPtest_logical4 +! NAN: fir.zero_bits !fir.logical<4> +! NAN: fir.store {{.*}} : !fir.ref<!fir.logical<4>> + +! HEX-LABEL: func.func @_QPtest_logical4 +! HEX: fir.zero_bits !fir.logical<4> +! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<4>> + +! --------------------------------------------------------------------------- +! CHARACTER(10) -- fir::CharacterType is not mlir::FloatType/IntegerType/ComplexType +! nan/snan/hex: fall back to fir.zero_bits (known limitation, TODO) +! --------------------------------------------------------------------------- +subroutine test_char10(res) + character(10) :: res + character(10) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_char10 +! ZERO: fir.zero_bits !fir.char<1,10> +! ZERO: fir.store {{.*}} : !fir.ref<!fir.char<1,10>> + +! NAN-LABEL: func.func @_QPtest_char10 +! NAN: fir.zero_bits !fir.char<1,10> +! NAN: fir.store {{.*}} : !fir.ref<!fir.char<1,10>> + +! SNAN-LABEL: func.func @_QPtest_char10 +! SNAN: fir.zero_bits !fir.char<1,10> +! SNAN: fir.store {{.*}} : !fir.ref<!fir.char<1,10>> + +! HEX-LABEL: func.func @_QPtest_char10 +! HEX: fir.zero_bits !fir.char<1,10> +! HEX: fir.store {{.*}} : !fir.ref<!fir.char<1,10>> + +! --------------------------------------------------------------------------- +! Derived type -- struct with an INTEGER(4) and a REAL(4) field +! nan/hex: field-by-field walk (integer: 0xAA; real: NaN or bitcast) +! --------------------------------------------------------------------------- +subroutine test_derived(res) + type :: mytype + integer(4) :: i + real(4) :: r + end type + type(mytype) :: res + type(mytype) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_derived +! ZERO: fir.zero_bits !fir.type<{{.*}}> +! ZERO: fir.store {{.*}} : !fir.ref<!fir.type<{{.*}}>> + +! NAN-LABEL: func.func @_QPtest_derived +! NAN: fir.coordinate_of {{.*}} -> !fir.ref<i32> +! NAN: arith.constant {{.*}} : i32 +! NAN: fir.store {{.*}} : !fir.ref<i32> +! NAN: fir.coordinate_of {{.*}} -> !fir.ref<f32> +! NAN: arith.constant {{.*}} : f32 +! NAN: fir.store {{.*}} : !fir.ref<f32> + +! HEX-LABEL: func.func @_QPtest_derived +! HEX: fir.coordinate_of {{.*}} -> !fir.ref<i32> +! HEX: arith.constant {{.*}} : i32 +! HEX: fir.store {{.*}} : !fir.ref<i32> +! HEX: fir.coordinate_of {{.*}} -> !fir.ref<f32> +! HEX: arith.bitcast {{.*}} : i32 to f32 +! HEX: fir.store {{.*}} : !fir.ref<f32> + + +! --------------------------------------------------------------------------- +! Array INTEGER(4)(4) -- 1-D; filled via insert_on_range +! --------------------------------------------------------------------------- +subroutine test_int_array(res) + integer(4) :: res(4) + integer(4) :: x(4) + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int_array +! ZERO: fir.insert_on_range {{.*}} from (0) to (3) +! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>> + +! NAN-LABEL: func.func @_QPtest_int_array +! NAN: fir.insert_on_range {{.*}} from (0) to (3) +! NAN: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>> + +! HEX-LABEL: func.func @_QPtest_int_array +! HEX: fir.insert_on_range {{.*}} from (0) to (3) +! HEX: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>> + +! OFF-LABEL: func.func @_QPtest_int_array +! OFF-NOT: fir.insert_on_range + +! --------------------------------------------------------------------------- +! Array REAL(4)(4) -- 1-D; nan/snan: NaN element; hex: bitcast element +! --------------------------------------------------------------------------- +subroutine test_real_array(res) + real(4) :: res(4) + real(4) :: x(4) + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_real_array +! ZERO: fir.insert_on_range {{.*}} from (0) to (3) +! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> + +! NAN-LABEL: func.func @_QPtest_real_array +! NAN: fir.insert_on_range {{.*}} from (0) to (3) +! NAN: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> + +! SNAN-LABEL: func.func @_QPtest_real_array +! SNAN: fir.insert_on_range {{.*}} from (0) to (3) +! SNAN: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> + +! HEX-LABEL: func.func @_QPtest_real_array +! HEX: fir.insert_on_range {{.*}} from (0) to (3) +! HEX: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> + +! --------------------------------------------------------------------------- +! Array INTEGER(4)(3,4) -- 2-D; insert_on_range with two-dimension bounds +! --------------------------------------------------------------------------- +subroutine test_int_array_2d(res) + integer(4) :: res(3,4) + integer(4) :: x(3,4) + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_int_array_2d +! ZERO: fir.insert_on_range {{.*}} from (0, 0) to (2, 3) +! ZERO: fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>> + +! HEX-LABEL: func.func @_QPtest_int_array_2d +! HEX: fir.insert_on_range {{.*}} from (0, 0) to (2, 3) +! HEX: fir.store {{.*}} : !fir.ref<!fir.array<3x4xi32>> + +! --------------------------------------------------------------------------- +! Exclusion: explicit init (= 42) -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_explicit_init(res) + integer(4) :: res + integer(4) :: x = 42 + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_explicit_init +! ZERO-NOT: fir.zero_bits + +! NAN-LABEL: func.func @_QPtest_explicit_init +! NAN-NOT: arith.constant -1431655766 : i32 + +! HEX-LABEL: func.func @_QPtest_explicit_init +! HEX-NOT: arith.bitcast + +! --------------------------------------------------------------------------- +! Exclusion: DATA statement init -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_data_init(res) + integer(4) :: res + integer(4) :: x + data x /99/ + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_data_init +! ZERO-NOT: fir.zero_bits i32 + +! NAN-LABEL: func.func @_QPtest_data_init +! NAN-NOT: arith.constant -1431655766 : i32 + +! HEX-LABEL: func.func @_QPtest_data_init +! HEX-NOT: arith.bitcast + +! --------------------------------------------------------------------------- +! Exclusion: derived-type default component init -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_default_comp_init(res) + type :: inittype + integer(4) :: i = 7 + real(4) :: r = 3.14 + end type + type(inittype) :: res + type(inittype) :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_default_comp_init +! ZERO-NOT: fir.zero_bits + +! NAN-LABEL: func.func @_QPtest_default_comp_init +! NAN-NOT: arith.constant -1431655766 : i32 + +! HEX-LABEL: func.func @_QPtest_default_comp_init +! HEX-NOT: arith.bitcast + +! --------------------------------------------------------------------------- +! Exclusion: SAVE -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_save(res) + integer(4) :: res + integer(4), save :: x + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_save +! ZERO-NOT: fir.zero_bits i32 + +! HEX-LABEL: func.func @_QPtest_save +! HEX-NOT: arith.constant -1431655766 : i32 + +! --------------------------------------------------------------------------- +! Exclusion: dummy argument -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_dummy(x, res) + integer(4), intent(in) :: x + integer(4) :: res + res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_dummy +! ZERO-NOT: fir.zero_bits i32 + +! HEX-LABEL: func.func @_QPtest_dummy +! HEX-NOT: arith.constant -1431655766 : i32 + +! --------------------------------------------------------------------------- +! Exclusion: ALLOCATABLE -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_allocatable(res) + integer(4), allocatable :: x + integer(4) :: res + if (allocated(x)) res = x +end subroutine +! ZERO-LABEL: func.func @_QPtest_allocatable +! ZERO-NOT: fir.zero_bits i32 + +! HEX-LABEL: func.func @_QPtest_allocatable +! HEX-NOT: arith.constant -1431655766 : i32 + +! --------------------------------------------------------------------------- +! Exclusion: EQUIVALENCE -- must NOT be touched +! --------------------------------------------------------------------------- +subroutine test_equivalence(res) + integer(4) :: res + integer(4) :: x, y + equivalence (x, y) + res = x + y +end subroutine +! ZERO-LABEL: func.func @_QPtest_equivalence +! ZERO-NOT: fir.zero_bits +! ZERO: return + +! NAN-LABEL: func.func @_QPtest_equivalence +! NAN-NOT: arith.constant -1431655766 : i32 +! NAN: return + +! HEX-LABEL: func.func @_QPtest_equivalence +! HEX-NOT: arith.bitcast +! HEX: return diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp index 4d6b0a22f426e..9f2225ab28f6f 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -272,6 +272,19 @@ static llvm::cl::opt<bool> initGlobalZero( llvm::cl::desc("Zero initialize globals without default initialization"), llvm::cl::init(true)); +static llvm::cl::opt<std::string> initLocalMode( + "finit-local", + llvm::cl::desc( + "Initialize local variables without explicit or default initialization. " + "Accepts: zero, nan, snan, or 0x<hex-byte>."), + llvm::cl::init("")); + +static llvm::cl::opt<bool> initLocalZero( + "finit-local-zero", + llvm::cl::desc("Zero-initialize local variables without explicit or default " + "initialization (alias for -finit-local=zero)"), + llvm::cl::init(false)); + static llvm::cl::opt<bool> reallocateLHS("frealloc-lhs", llvm::cl::desc("Follow Fortran 2003 rules for (re)allocating " @@ -501,6 +514,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<uint8_t>(hexVal)); + } else { + llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n"; + } + } else { + llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n"; + } + } loweringOptions.setReallocateLHS(reallocateLHS); loweringOptions.setStackRepackArrays(stackRepackArrays); loweringOptions.setRepackArrays(repackArrays); _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
