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 <[email protected]> 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<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..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=<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..7f24c02c57c79 100644 --- a/flang/include/flang/Lower/LoweringOptions.h +++ b/flang/include/flang/Lower/LoweringOptions.h @@ -17,9 +17,20 @@ #include "flang/Support/FPMaxminBehavior.h" #include "flang/Support/MathOptionsBase.h" +#include <cstdint> 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 +63,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..5380a51a16345 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -1755,6 +1755,33 @@ 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..03ebe82cb52ce 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -19,6 +19,7 @@ #include "flang/Lower/ConvertConstant.h" #include "flang/Lower/ConvertExprToHLFIR.h" #include "flang/Lower/ConvertProcedureDesignator.h" +#include "flang/Lower/LoweringOptions.h" #include "flang/Lower/Mangler.h" #include "flang/Lower/MultiImageFortran.h" #include "flang/Lower/OpenACC.h" @@ -45,7 +46,10 @@ #include "flang/Runtime/allocator-registry-consts.h" #include "flang/Semantics/tools.h" #include "flang/Semantics/type.h" +#include "mlir/Dialect/Complex/IR/Complex.h" #include "mlir/Dialect/OpenACC/OpenACC.h" +#include "llvm/ADT/APFloat.h" +#include "llvm/ADT/APInt.h" #include "llvm/ADT/SmallVector.h" #include "llvm/Support/CommandLine.h" #include "llvm/Support/Debug.h" @@ -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, + 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 +1507,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..0bf66c72c05a7 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -272,6 +272,20 @@ 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 +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<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); >From 4c585dbf413e83979aef020275bf07415977364b Mon Sep 17 00:00:00 2001 From: Daniel Chen <[email protected]> Date: Fri, 14 Aug 2026 10:34:37 -0400 Subject: [PATCH 2/2] To address tarunprabhu's review comments. --- clang/include/clang/Options/FlangOptions.td | 4 --- flang/docs/ReleaseNotes.md | 2 +- flang/lib/Lower/ConvertVariable.cpp | 14 +++++---- flang/test/Driver/finit-local.f90 | 35 ++++++++------------- flang/test/Lower/finit-local-f128.f90 | 8 ++--- 5 files changed, 26 insertions(+), 37 deletions(-) diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td index d3402467b6f8e..881bf58aa1e3f 100644 --- a/clang/include/clang/Options/FlangOptions.td +++ b/clang/include/clang/Options/FlangOptions.td @@ -393,10 +393,6 @@ 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]>, diff --git a/flang/docs/ReleaseNotes.md b/flang/docs/ReleaseNotes.md index 660166e8099d0..3cc8794785803 100644 --- a/flang/docs/ReleaseNotes.md +++ b/flang/docs/ReleaseNotes.md @@ -57,7 +57,7 @@ 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) +- Added `-finit-local=<val>` to initialize automatic local 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`. diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index 03ebe82cb52ce..202222d299982 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -1420,26 +1420,28 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter, elePat = genByteSplatInit(builder, loc, eleTy, hexByte); break; case Fortran::lower::InitLocalKind::QNaN: - if (fpTy) + if (fpTy) { elePat = genFPNaNInit(builder, loc, fpTy, false); - else if (cplxTy) { + } 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 + } else { elePat = genByteSplatInit(builder, loc, eleTy, 0xAA); + } break; case Fortran::lower::InitLocalKind::SNaN: - if (fpTy) + if (fpTy) { elePat = genFPNaNInit(builder, loc, fpTy, true); - else if (cplxTy) { + } 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 + } else { elePat = genByteSplatInit(builder, loc, eleTy, 0xAA); + } break; default: llvm_unreachable("unexpected InitLocalKind"); diff --git a/flang/test/Driver/finit-local.f90 b/flang/test/Driver/finit-local.f90 index 01f288b7a8803..1d39a83b4ce01 100644 --- a/flang/test/Driver/finit-local.f90 +++ b/flang/test/Driver/finit-local.f90 @@ -2,32 +2,23 @@ ! 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 +! RUN: %flang -### -S -finit-local=zero %s 2>&1 | FileCheck --check-prefix=ZERO %s +! RUN: %flang -### -S -finit-local=nan %s 2>&1 | FileCheck --check-prefix=NAN %s +! RUN: %flang -### -S -finit-local=snan %s 2>&1 | FileCheck --check-prefix=SNAN %s +! RUN: %flang -### -S -finit-local=0xAA %s 2>&1 | FileCheck --check-prefix=HEX %s +! RUN: %flang -### -S -finit-local=0xff %s 2>&1 | FileCheck --check-prefix=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 - - +! RUN: %flang -### -S -finit-local-zero %s 2>&1 | FileCheck --check-prefix=ZERO %s ! --- 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 +! RUN: not %flang_fc1 -emit-hlfir -finit-local=bogus %s 2>&1 | FileCheck --check-prefix=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' +! ZERO: "-fc1"{{.*}} "-finit-local=zero" +! NAN: "-fc1"{{.*}} "-finit-local=nan" +! SNAN: "-fc1"{{.*}} "-finit-local=snan" +! HEX: "-fc1"{{.*}} "-finit-local=0xAA" +! HEX2: "-fc1"{{.*}} "-finit-local=0xff" +! 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 index a59322c6df139..69b6af69e4cee 100644 --- a/flang/test/Lower/finit-local-f128.f90 +++ b/flang/test/Lower/finit-local-f128.f90 @@ -3,10 +3,10 @@ ! ! 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 +! RUN: %flang_fc1 -emit-hlfir -finit-local=zero %s -o - | FileCheck --check-prefix=ZERO %s +! RUN: %flang_fc1 -emit-hlfir -finit-local=nan %s -o - | FileCheck --check-prefix=NAN %s +! RUN: %flang_fc1 -emit-hlfir -finit-local=snan %s -o - | FileCheck --check-prefix=SNAN %s +! RUN: %flang_fc1 -emit-hlfir -finit-local=0xAA %s -o - | FileCheck --check-prefix=HEX %s ! --------------------------------------------------------------------------- ! REAL(16) -- 16-byte FP (f128); hex uses 128-bit APInt splat + bitcast _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
