https://github.com/DanielCChen updated https://github.com/llvm/llvm-project/pull/216164
>From 940ae6b6b2379a76c4bbddf3c8a94cd7a58ee34e Mon Sep 17 00:00:00 2001 From: Daniel Chen <[email protected]> Date: Thu, 13 Aug 2026 15:30:12 -0400 Subject: [PATCH 1/4] [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 98b4b99f3a5ac23f22007d44028554179b2ccc8f Mon Sep 17 00:00:00 2001 From: Daniel Chen <[email protected]> Date: Fri, 14 Aug 2026 10:34:37 -0400 Subject: [PATCH 2/4] [flang] Address tarunprabhu's review comments on -finit-local= - FlangOptions.td: remove redundant block comment above finit_local_EQ. - ReleaseNotes.md: drop "stack-allocated" from the description. - ConvertVariable.cpp: add braces to if/else branches in the QNaN and SNaN cases per LLVM coding standards. - flang/test/Driver/finit-local.f90: drop -o -, rename CHECK-* prefixes, remove redundant fc1-acceptance RUN lines. - flang/test/Lower/finit-local-f128.f90: switch from bbc to %flang_fc1. --- 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 >From e1795d36b98e3bddd69149d83a3ed11ea9065999 Mon Sep 17 00:00:00 2001 From: Daniel Chen <[email protected]> Date: Fri, 14 Aug 2026 13:14:20 -0400 Subject: [PATCH 3/4] [flang] Fix -finit-local= issues found in first review round - Refactor genInitLocal to use a recursive initAddr lambda so that array-valued derived-type fields dispatch correctly through the array path instead of having their sequence type stripped. - Skip zero-extent arrays in shouldInitLocal. Exclude all CUDA storage. - Add LOGICAL byte-splat support in genByteSplatInit. - bbc: make -finit-local-zero and -finit-local= last-wins; invalid values now return a non-zero exit code. - finit-local-f128.f90: accept both !fir.complex<16> and complex<f128>. --- flang/lib/Lower/ConvertVariable.cpp | 178 +++++++++++++++----------- flang/test/Lower/finit-local-f128.f90 | 10 +- flang/test/Lower/finit-local.f90 | 12 +- flang/tools/bbc/bbc.cpp | 14 +- 4 files changed, 122 insertions(+), 92 deletions(-) diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index 202222d299982..bbb65619935f4 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -1280,6 +1280,10 @@ static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) { return false; if (Fortran::semantics::FindEquivalenceSet(sym)) return false; + // CUDA device/managed/unified/shared/pinned variables must not be + // initialized with a plain host store; their storage lives in device memory. + if (Fortran::semantics::GetCUDADataAttr(&sym)) + return false; return true; } @@ -1316,6 +1320,11 @@ static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder, return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal, partVal); } + // LOGICAL(k) has a fixed size of k bytes; treat it like an integer splat. + if (auto logTy = mlir::dyn_cast<fir::LogicalType>(eleTy)) { + mlir::Value intCst = makeIntCst(logTy.getFKind() * 8); + return builder.createConvert(loc, logTy, intCst); + } // 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); @@ -1406,84 +1415,97 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter, 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); - } + // Recursive helper: dispatch on type to initialize the storage at \p addr + // of type \p ty. Handles arrays, derived types, and scalars. + std::function<void(mlir::Type, mlir::Value)> initAddr = + [&](mlir::Type ty, mlir::Value addr) { + if (auto seqTy = mlir::dyn_cast<fir::SequenceType>(ty)) { + // 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; + // Skip CHARACTER arrays: fir.zero_bits is not a valid insert_on_range + // element for character types (runtime-length or fixed). CHARACTER + // initialization is a known TODO. + bool hasUnknown = mlir::isa<fir::CharacterType>(eleTy); + for (auto dim : seqTy.getShape()) { + if (dim == fir::SequenceType::getUnknownExtent() || dim == 0) { + 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, addr); + } + } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) { + // 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), addr); + } 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), addr, + mlir::ValueRange{fieldIdx}); + initAddr(fieldTy, fieldAddr); + } + } + } else { + // Scalar (integer, real, complex, logical, character): store + // directly. + genInitLocalStore(builder, loc, ty, addr, mode, hexByte); + } + }; + initAddr(storeTy, base); } /// Instantiate a local variable. Precondition: Each variable will be visited diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90 index 69b6af69e4cee..b8157e8dbd1e8 100644 --- a/flang/test/Lower/finit-local-f128.f90 +++ b/flang/test/Lower/finit-local-f128.f90 @@ -44,21 +44,21 @@ subroutine test_complex16(res) 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>> +! ZERO: fir.zero_bits {{!fir\.complex<16>|complex<f128>}} +! ZERO: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> ! NAN-LABEL: func.func @_QPtest_complex16 ! NAN: arith.constant {{.*}} : f128 ! NAN: complex.create {{.*}}, {{.*}} : f128 -! NAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>> +! NAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> ! SNAN-LABEL: func.func @_QPtest_complex16 ! SNAN: arith.constant {{.*}} : f128 ! SNAN: complex.create {{.*}}, {{.*}} : f128 -! SNAN: fir.store {{.*}} : !fir.ref<!fir.complex<16>> +! SNAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> ! 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>> +! HEX: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90 index 379a7842dd0ac..fe2a4adca4066 100644 --- a/flang/test/Lower/finit-local.f90 +++ b/flang/test/Lower/finit-local.f90 @@ -228,11 +228,13 @@ subroutine test_logical1(res) ! ZERO: fir.zero_bits !fir.logical<1> ! NAN-LABEL: func.func @_QPtest_logical1 -! NAN: fir.zero_bits !fir.logical<1> +! NAN: arith.constant -86 : i8 +! NAN: fir.convert {{.*}} : (i8) -> !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: arith.constant {{.*}} : i8 +! HEX: fir.convert {{.*}} : (i8) -> !fir.logical<1> ! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<1>> ! --------------------------------------------------------------------------- @@ -247,11 +249,13 @@ subroutine test_logical4(res) ! ZERO: fir.zero_bits !fir.logical<4> ! NAN-LABEL: func.func @_QPtest_logical4 -! NAN: fir.zero_bits !fir.logical<4> +! NAN: arith.constant -1431655766 : i32 +! NAN: fir.convert {{.*}} : (i32) -> !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: arith.constant {{.*}} : i32 +! HEX: fir.convert {{.*}} : (i32) -> !fir.logical<4> ! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<4>> ! --------------------------------------------------------------------------- diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp index 0bf66c72c05a7..4adbb5e7764d4 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -515,11 +515,8 @@ 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= + // -finit-local= and -finit-local-zero: last occurrence on the command + // line wins. Use getPosition() to determine which came last. if (!initLocalMode.empty()) { llvm::StringRef val = initLocalMode; if (val == "zero") { @@ -535,11 +532,18 @@ static llvm::LogicalResult convertFortranSourceToMLIR( loweringOptions.setInitLocalPattern(static_cast<uint8_t>(hexVal)); } else { llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n"; + return mlir::failure(); } } else { llvm::errs() << "bbc: invalid -finit-local= value: " << val << "\n"; + return mlir::failure(); } } + // If -finit-local-zero appears after -finit-local= on the command line, + // it overrides; otherwise -finit-local= already set the mode above. + if (initLocalZero && + initLocalZero.getPosition() > initLocalMode.getPosition()) + loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero); loweringOptions.setReallocateLHS(reallocateLHS); loweringOptions.setStackRepackArrays(stackRepackArrays); loweringOptions.setRepackArrays(repackArrays); >From 7c30be78bbee3b4e1527c6c64402af04e236a7da Mon Sep 17 00:00:00 2001 From: Daniel Chen <[email protected]> Date: Sat, 15 Aug 2026 11:05:00 -0400 Subject: [PATCH 4/4] [flang] Fix -finit-local= correctness issues found in review - LOGICAL: store the raw iN value via a bitcasted address; fir.convert to !fir.logical normalizes any nonzero integer to 1. - Arrays: use fir.do_loop + fir.coordinate_of for non-zero modes to avoid llvm.mlir.constant rejecting non-zero ArrayAttr; skip zero-extent and CHARACTER arrays. - CUDA: allow pinned/unified initialization; exclude only device, managed, constant, shared, and usedevice storage. - bbc: reject an explicitly empty -finit-local= value with non-zero exit code. - finit-local-f128.f90: require complex<f128> exactly. - Add finit-local-logical-llvm.f90, finit-local-array-llvm.f90, finit-local-cuda.cuf regression tests. --- flang/lib/Lower/ConvertVariable.cpp | 99 ++++++++++++++----- flang/test/Lower/CUDA/finit-local-cuda.cuf | 40 ++++++++ flang/test/Lower/finit-local-array-llvm.f90 | 48 +++++++++ flang/test/Lower/finit-local-f128.f90 | 16 +-- flang/test/Lower/finit-local-logical-llvm.f90 | 46 +++++++++ flang/test/Lower/finit-local.f90 | 52 ++++++---- flang/tools/bbc/bbc.cpp | 6 +- 7 files changed, 253 insertions(+), 54 deletions(-) create mode 100644 flang/test/Lower/CUDA/finit-local-cuda.cuf create mode 100644 flang/test/Lower/finit-local-array-llvm.f90 create mode 100644 flang/test/Lower/finit-local-logical-llvm.f90 diff --git a/flang/lib/Lower/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp index bbb65619935f4..c1a3df42d1690 100644 --- a/flang/lib/Lower/ConvertVariable.cpp +++ b/flang/lib/Lower/ConvertVariable.cpp @@ -1280,15 +1280,25 @@ static bool shouldInitLocal(const Fortran::lower::pft::Variable &var) { return false; if (Fortran::semantics::FindEquivalenceSet(sym)) return false; - // CUDA device/managed/unified/shared/pinned variables must not be - // initialized with a plain host store; their storage lives in device memory. - if (Fortran::semantics::GetCUDADataAttr(&sym)) - return false; + // Skip CUDA variables whose storage is not host-accessible via a plain + // store: device, managed, constant, shared, and usedevice all live in + // device memory. Pinned and unified memory are host-accessible and may + // be initialized normally. + if (auto cudaAttr = Fortran::semantics::GetCUDADataAttr(&sym)) { + if (*cudaAttr == Fortran::common::CUDADataAttr::Device || + *cudaAttr == Fortran::common::CUDADataAttr::Managed || + *cudaAttr == Fortran::common::CUDADataAttr::Constant || + *cudaAttr == Fortran::common::CUDADataAttr::Shared || + *cudaAttr == Fortran::common::CUDADataAttr::UseDevice) { + 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. +/// LOGICAL(k): returns a raw iN integer (caller stores via bitcasted address). /// 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, @@ -1320,10 +1330,11 @@ static mlir::Value genByteSplatInit(fir::FirOpBuilder &builder, return mlir::complex::CreateOp::create(builder, loc, cplxTy, partVal, partVal); } - // LOGICAL(k) has a fixed size of k bytes; treat it like an integer splat. + // LOGICAL(k) has a fixed size of k bytes. Return the raw integer splat; + // the caller stores it via a bitcasted address to preserve the bit pattern + // (fir.convert from integer to !fir.logical normalizes nonzero -> true). if (auto logTy = mlir::dyn_cast<fir::LogicalType>(eleTy)) { - mlir::Value intCst = makeIntCst(logTy.getFKind() * 8); - return builder.createConvert(loc, logTy, intCst); + return makeIntCst(logTy.getFKind() * 8); } // TODO: CHARACTER falls back to zero; a future improvement should fill each // storage unit with the byte pattern. @@ -1345,8 +1356,9 @@ static mlir::Value genFPNaNInit(fir::FirOpBuilder &builder, mlir::Location loc, } /// 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. +/// Complex types get NaN on both parts; integer/logical non-FP types use a +/// 0xAA byte-splat for nan/snan modes. LOGICAL stores via a bitcasted integer +/// address to preserve the raw bit pattern past fir.convert normalization. static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc, mlir::Type ty, mlir::Value addr, Fortran::lower::InitLocalKind mode, @@ -1390,12 +1402,24 @@ static void genInitLocalStore(fir::FirOpBuilder &builder, mlir::Location loc, default: llvm_unreachable("unexpected InitLocalKind in genInitLocalStore"); } - fir::StoreOp::create(builder, loc, val, addr); + // For LOGICAL types, genByteSplatInit returns a raw integer to preserve + // the bit pattern. Store it via a bitcasted address to avoid fir.convert + // normalization (which would reduce any nonzero value to logical true). + if (mode != Fortran::lower::InitLocalKind::Zero && + mlir::isa<fir::LogicalType>(ty)) { + unsigned bits = mlir::cast<fir::LogicalType>(ty).getFKind() * 8; + mlir::Type intRefTy = builder.getRefType(builder.getIntegerType(bits)); + mlir::Value intAddr = builder.createConvert(loc, intRefTy, addr); + fir::StoreOp::create(builder, loc, val, intAddr); + } else { + 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. +/// Arrays: zero mode uses insert_on_range; non-zero modes use a do_loop + +/// coordinate_of to avoid llvm.mlir.constant rejecting non-zero ArrayAttrs. +/// 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) { @@ -1461,26 +1485,53 @@ static void genInitLocal(Fortran::lower::AbstractConverter &converter, default: llvm_unreachable("unexpected InitLocalKind"); } - // Build flat [lb0,ub0, lb1,ub1, ...] bounds vector. - llvm::SmallVector<int64_t> rangeBounds; - // Skip CHARACTER arrays: fir.zero_bits is not a valid insert_on_range - // element for character types (runtime-length or fixed). CHARACTER - // initialization is a known TODO. + // Compute static extents; skip arrays with unknown or zero extents, + // and CHARACTER arrays (known TODO, pending PR #159788). bool hasUnknown = mlir::isa<fir::CharacterType>(eleTy); + int64_t totalElems = 1; for (auto dim : seqTy.getShape()) { if (dim == fir::SequenceType::getUnknownExtent() || dim == 0) { hasUnknown = true; break; } - rangeBounds.push_back(0); - rangeBounds.push_back(dim - 1); + totalElems *= dim; } 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, addr); + if (mode == Fortran::lower::InitLocalKind::Zero) { + // Zero mode: fir.insert_on_range + store works correctly through + // LLVM lowering (ZeroAttr is accepted by llvm.mlir.constant). + llvm::SmallVector<int64_t> rangeBounds; + for (auto dim : seqTy.getShape()) { + rangeBounds.push_back(0); + rangeBounds.push_back(dim - 1); + } + 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, addr); + } else { + // Non-zero modes: fir.insert_on_range fails at LLVM lowering + // because llvm.mlir.constant does not accept ArrayAttr of + // non-zero scalars. Use a flat do_loop + coordinate_of instead. + mlir::Type idxTy = builder.getIndexType(); + mlir::Value zero = + builder.createIntegerConstant(loc, idxTy, 0); + mlir::Value last = + builder.createIntegerConstant(loc, idxTy, totalElems - 1); + mlir::Value one = + builder.createIntegerConstant(loc, idxTy, 1); + auto loop = fir::DoLoopOp::create(builder, loc, zero, last, one, + /*unordered=*/false, + /*finalCount=*/false); + mlir::OpBuilder::InsertionGuard guard(builder); + builder.setInsertionPointToStart(loop.getBody()); + mlir::Value iv = loop.getInductionVar(); + mlir::Value elemAddr = fir::CoordinateOp::create( + builder, loc, builder.getRefType(eleTy), addr, + mlir::ValueRange{iv}); + genInitLocalStore(builder, loc, eleTy, elemAddr, mode, hexByte); + } } } else if (auto recTy = mlir::dyn_cast<fir::RecordType>(ty)) { // Derived type: zero the whole struct, or walk fields for diff --git a/flang/test/Lower/CUDA/finit-local-cuda.cuf b/flang/test/Lower/CUDA/finit-local-cuda.cuf new file mode 100644 index 0000000000000..3b45c4242df0f --- /dev/null +++ b/flang/test/Lower/CUDA/finit-local-cuda.cuf @@ -0,0 +1,40 @@ +! Tests that -finit-local= respects CUDA data attributes in shouldInitLocal. +! Variables in device-only memory (device, managed, constant, shared) must NOT +! be initialized via a plain host store. Host-accessible storage (pinned, +! unified) MAY be initialized normally. +! +! RUN: bbc -emit-hlfir -fcuda -finit-local=zero -o - %s | FileCheck %s + +module finit_cuda_mod +contains + + ! device: storage lives in device memory -- must NOT be initialized. + subroutine test_device(res) + real, device :: x + real :: res + res = x + end subroutine + ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_device + ! CHECK-NOT: fir.zero_bits f32 + + ! unified: host-accessible -- MUST be initialized. + subroutine test_unified(res) + real, unified :: x + real :: res + res = x + end subroutine + ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_unified + ! CHECK: fir.zero_bits f32 + ! CHECK: fir.store + + ! pinned (non-allocatable): host-accessible -- MUST be initialized. + subroutine test_pinned(res) + real, pinned :: x + real :: res + res = x + end subroutine + ! CHECK-LABEL: func.func @_QMfinit_cuda_modPtest_pinned + ! CHECK: fir.zero_bits f32 + ! CHECK: fir.store + +end module diff --git a/flang/test/Lower/finit-local-array-llvm.f90 b/flang/test/Lower/finit-local-array-llvm.f90 new file mode 100644 index 0000000000000..ff777639131e2 --- /dev/null +++ b/flang/test/Lower/finit-local-array-llvm.f90 @@ -0,0 +1,48 @@ +! Tests that -finit-local= with non-zero patterns produces correct LLVM IR for +! static arrays and arrays inside derived types. Previously, fir.insert_on_range +! with a non-zero element failed at LLVM lowering because llvm.mlir.constant +! does not accept ArrayAttr of non-zero scalars. The fix uses a do_loop + +! coordinate_of instead, which lowers correctly through to LLVM IR. +! +! RUN: %flang_fc1 -emit-llvm -finit-local=0xAA %s -o - | FileCheck --check-prefix=HEX %s +! RUN: %flang_fc1 -emit-llvm -finit-local=nan %s -o - | FileCheck --check-prefix=NAN %s +! RUN: %flang_fc1 -emit-llvm -finit-local=zero %s -o - | FileCheck --check-prefix=ZERO %s + +! --------------------------------------------------------------------------- +! Static 1-D array INTEGER(4)(4) +! --------------------------------------------------------------------------- +subroutine test_int_array(res) + integer(4) :: res(4) + integer(4) :: x(4) + res = x +end subroutine +! HEX-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}( +! HEX: store i32 -1431655766, +! HEX-NOT: store i32 0, + +! NAN-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}( +! NAN: store i32 -1431655766, + +! ZERO-LABEL: define {{.*}}@{{.*}}test_int_array{{.*}}( +! ZERO: store [4 x i32] zeroinitializer, + +! --------------------------------------------------------------------------- +! Derived type with an array-valued field (Thread 2 regression) +! type t; integer :: a(2); end type; type(t) :: x +! --------------------------------------------------------------------------- +subroutine test_array_in_struct(res) + type :: t + integer(4) :: a(2) + end type + type(t) :: res + type(t) :: x + res = x +end subroutine +! HEX-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}( +! HEX: store i32 -1431655766, + +! NAN-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}( +! NAN: store i32 -1431655766, + +! ZERO-LABEL: define {{.*}}@{{.*}}test_array_in_struct{{.*}}( +! ZERO: store {{.*}} zeroinitializer, diff --git a/flang/test/Lower/finit-local-f128.f90 b/flang/test/Lower/finit-local-f128.f90 index b8157e8dbd1e8..937b2e86bb649 100644 --- a/flang/test/Lower/finit-local-f128.f90 +++ b/flang/test/Lower/finit-local-f128.f90 @@ -44,21 +44,21 @@ subroutine test_complex16(res) res = x end subroutine ! ZERO-LABEL: func.func @_QPtest_complex16 -! ZERO: fir.zero_bits {{!fir\.complex<16>|complex<f128>}} -! ZERO: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> +! ZERO: fir.zero_bits complex<f128> +! ZERO: fir.store {{.*}} : !fir.ref<complex<f128>> ! NAN-LABEL: func.func @_QPtest_complex16 ! NAN: arith.constant {{.*}} : f128 -! NAN: complex.create {{.*}}, {{.*}} : f128 -! NAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> +! NAN: complex.create {{.*}}, {{.*}} : complex<f128> +! NAN: fir.store {{.*}} : !fir.ref<complex<f128>> ! SNAN-LABEL: func.func @_QPtest_complex16 ! SNAN: arith.constant {{.*}} : f128 -! SNAN: complex.create {{.*}}, {{.*}} : f128 -! SNAN: fir.store {{.*}} : !fir.ref<{{!fir\.complex<16>|complex<f128>}}> +! SNAN: complex.create {{.*}}, {{.*}} : complex<f128> +! SNAN: fir.store {{.*}} : !fir.ref<complex<f128>> ! 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>|complex<f128>}}> +! HEX: complex.create {{.*}}, {{.*}} : complex<f128> +! HEX: fir.store {{.*}} : !fir.ref<complex<f128>> diff --git a/flang/test/Lower/finit-local-logical-llvm.f90 b/flang/test/Lower/finit-local-logical-llvm.f90 new file mode 100644 index 0000000000000..6f41b78883b59 --- /dev/null +++ b/flang/test/Lower/finit-local-logical-llvm.f90 @@ -0,0 +1,46 @@ +! Tests that -finit-local= preserves the requested bit pattern for LOGICAL +! variables in the final LLVM IR. fir.convert from integer to !fir.logical +! normalizes any nonzero value to .TRUE. (i.e. 1); the fix stores via a +! bitcasted integer address instead so the bit pattern is preserved. +! +! RUN: %flang_fc1 -emit-llvm -finit-local=0xAA %s -o - | FileCheck --check-prefix=HEX %s +! RUN: %flang_fc1 -emit-llvm -finit-local=nan %s -o - | FileCheck --check-prefix=NAN %s +! RUN: %flang_fc1 -emit-llvm -finit-local=zero %s -o - | FileCheck --check-prefix=ZERO %s + +! --------------------------------------------------------------------------- +! LOGICAL(1) -- 1-byte storage; 0xAA byte-splat = -86 (i8), NOT i8 1 +! --------------------------------------------------------------------------- +subroutine test_logical1(res) + logical(1) :: res + logical(1) :: x + res = x +end subroutine +! HEX-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}( +! HEX: store i8 -86, +! HEX-NOT: store i8 1, + +! NAN-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}( +! NAN: store i8 -86, +! NAN-NOT: store i8 1, + +! ZERO-LABEL: define {{.*}}@{{.*}}test_logical1{{.*}}( +! ZERO: store i8 0, + +! --------------------------------------------------------------------------- +! LOGICAL(4) -- 4-byte storage; 0xAA byte-splat = -1431655766 (i32), NOT i32 1 +! --------------------------------------------------------------------------- +subroutine test_logical4(res) + logical(4) :: res + logical(4) :: x + res = x +end subroutine +! HEX-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}( +! HEX: store i32 -1431655766, +! HEX-NOT: store i32 1, + +! NAN-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}( +! NAN: store i32 -1431655766, +! NAN-NOT: store i32 1, + +! ZERO-LABEL: define {{.*}}@{{.*}}test_logical4{{.*}}( +! ZERO: store i32 0, diff --git a/flang/test/Lower/finit-local.f90 b/flang/test/Lower/finit-local.f90 index fe2a4adca4066..752b9c4f84c7d 100644 --- a/flang/test/Lower/finit-local.f90 +++ b/flang/test/Lower/finit-local.f90 @@ -17,6 +17,10 @@ ! 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 +! --- Empty value should be rejected by bbc --- +! RUN: not bbc -emit-hlfir -finit-local= -o - %s 2>&1 | FileCheck --check-prefix=EMPTY %s + +! EMPTY: bbc: invalid -finit-local= value: (empty) ! --------------------------------------------------------------------------- ! INTEGER(1) -- 1-byte: pattern 0xAA = -86 (signed) = 170 (unsigned) @@ -229,13 +233,13 @@ subroutine test_logical1(res) ! NAN-LABEL: func.func @_QPtest_logical1 ! NAN: arith.constant -86 : i8 -! NAN: fir.convert {{.*}} : (i8) -> !fir.logical<1> -! NAN: fir.store {{.*}} : !fir.ref<!fir.logical<1>> +! NAN: fir.convert {{.*}} : (!fir.ref<!fir.logical<1>>) -> !fir.ref<i8> +! NAN: fir.store {{.*}} : !fir.ref<i8> ! HEX-LABEL: func.func @_QPtest_logical1 ! HEX: arith.constant {{.*}} : i8 -! HEX: fir.convert {{.*}} : (i8) -> !fir.logical<1> -! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<1>> +! HEX: fir.convert {{.*}} : (!fir.ref<!fir.logical<1>>) -> !fir.ref<i8> +! HEX: fir.store {{.*}} : !fir.ref<i8> ! --------------------------------------------------------------------------- ! LOGICAL(4) -- stored as i32; pattern 0xAAAAAAAA = -1431655766 @@ -250,13 +254,13 @@ subroutine test_logical4(res) ! NAN-LABEL: func.func @_QPtest_logical4 ! NAN: arith.constant -1431655766 : i32 -! NAN: fir.convert {{.*}} : (i32) -> !fir.logical<4> -! NAN: fir.store {{.*}} : !fir.ref<!fir.logical<4>> +! NAN: fir.convert {{.*}} : (!fir.ref<!fir.logical<4>>) -> !fir.ref<i32> +! NAN: fir.store {{.*}} : !fir.ref<i32> ! HEX-LABEL: func.func @_QPtest_logical4 ! HEX: arith.constant {{.*}} : i32 -! HEX: fir.convert {{.*}} : (i32) -> !fir.logical<4> -! HEX: fir.store {{.*}} : !fir.ref<!fir.logical<4>> +! HEX: fir.convert {{.*}} : (!fir.ref<!fir.logical<4>>) -> !fir.ref<i32> +! HEX: fir.store {{.*}} : !fir.ref<i32> ! --------------------------------------------------------------------------- ! CHARACTER(10) -- fir::CharacterType is not mlir::FloatType/IntegerType/ComplexType @@ -330,12 +334,14 @@ subroutine test_int_array(res) ! 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>> +! NAN: fir.do_loop +! NAN: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xi32>>, index) -> !fir.ref<i32> +! NAN: fir.store {{.*}} : !fir.ref<i32> ! HEX-LABEL: func.func @_QPtest_int_array -! HEX: fir.insert_on_range {{.*}} from (0) to (3) -! HEX: fir.store {{.*}} : !fir.ref<!fir.array<4xi32>> +! HEX: fir.do_loop +! HEX: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xi32>>, index) -> !fir.ref<i32> +! HEX: fir.store {{.*}} : !fir.ref<i32> ! OFF-LABEL: func.func @_QPtest_int_array ! OFF-NOT: fir.insert_on_range @@ -353,19 +359,22 @@ subroutine test_real_array(res) ! 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>> +! NAN: fir.do_loop +! NAN: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32> +! NAN: fir.store {{.*}} : !fir.ref<f32> ! SNAN-LABEL: func.func @_QPtest_real_array -! SNAN: fir.insert_on_range {{.*}} from (0) to (3) -! SNAN: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> +! SNAN: fir.do_loop +! SNAN: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32> +! SNAN: fir.store {{.*}} : !fir.ref<f32> ! HEX-LABEL: func.func @_QPtest_real_array -! HEX: fir.insert_on_range {{.*}} from (0) to (3) -! HEX: fir.store {{.*}} : !fir.ref<!fir.array<4xf32>> +! HEX: fir.do_loop +! HEX: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<4xf32>>, index) -> !fir.ref<f32> +! HEX: fir.store {{.*}} : !fir.ref<f32> ! --------------------------------------------------------------------------- -! Array INTEGER(4)(3,4) -- 2-D; insert_on_range with two-dimension bounds +! Array INTEGER(4)(3,4) -- 2-D; zero uses insert_on_range, hex uses do_loop ! --------------------------------------------------------------------------- subroutine test_int_array_2d(res) integer(4) :: res(3,4) @@ -377,8 +386,9 @@ subroutine test_int_array_2d(res) ! 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>> +! HEX: fir.do_loop +! HEX: fir.coordinate_of {{.*}} : (!fir.ref<!fir.array<3x4xi32>>, index) -> !fir.ref<i32> +! HEX: fir.store {{.*}} : !fir.ref<i32> ! --------------------------------------------------------------------------- ! Exclusion: explicit init (= 42) -- must NOT be touched diff --git a/flang/tools/bbc/bbc.cpp b/flang/tools/bbc/bbc.cpp index 4adbb5e7764d4..8fd72df336ab3 100644 --- a/flang/tools/bbc/bbc.cpp +++ b/flang/tools/bbc/bbc.cpp @@ -517,8 +517,12 @@ static llvm::LogicalResult convertFortranSourceToMLIR( loweringOptions.setInitGlobalZero(initGlobalZero); // -finit-local= and -finit-local-zero: last occurrence on the command // line wins. Use getPosition() to determine which came last. - if (!initLocalMode.empty()) { + if (initLocalMode.getNumOccurrences() > 0) { llvm::StringRef val = initLocalMode; + if (val.empty()) { + llvm::errs() << "bbc: invalid -finit-local= value: (empty)\n"; + return mlir::failure(); + } if (val == "zero") { loweringOptions.setInitLocalMode(Fortran::lower::InitLocalKind::Zero); } else if (val == "nan") { _______________________________________________ cfe-commits mailing list [email protected] https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits
