diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index e2ab046b26ae6..9b6ab62e13255 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -6960,6 +6960,7 @@ def A_DASH : Joined<["-"], "A-">, Group; def static_libgfortran : Flag<["-"], "static-libgfortran">, Group; // "f" options with values for gfortran. +// Some of these options are visible for LLVM Flang too. def fblas_matmul_limit_EQ : Joined<["-"], "fblas-matmul-limit=">, Group; def fcheck_EQ : Joined<["-"], "fcheck=">, Group; def fcoarray_EQ : Joined<["-"], "fcoarray=">, Group; @@ -6967,7 +6968,10 @@ def ffpe_trap_EQ : Joined<["-"], "ffpe-trap=">, Group; def ffree_line_length_VALUE : Joined<["-"], "ffree-line-length-">, Group; def finit_character_EQ : Joined<["-"], "finit-character=">, Group; def finit_integer_EQ : Joined<["-"], "finit-integer=">, Group; -def finit_logical_EQ : Joined<["-"], "finit-logical=">, Group; +def finit_logical_EQ : Joined<["-"], "finit-logical=">, + Group, + Visibility<[FlangOption, FC1Option]>, + HelpText<"Initialize logical type.">; def finit_real_EQ : Joined<["-"], "finit-real=">, Group; def fmax_array_constructor_EQ : Joined<["-"], "fmax-array-constructor=">, Group; def fmax_errors_EQ : Joined<["-"], "fmax-errors=">, Group; diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index 7ab41e9b85a04..8478ce4ac57d5 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -172,8 +172,8 @@ void Flang::addCodegenOptions(const ArgList &Args, options::OPT_flang_deprecated_no_hlfir, 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_finit_logical_EQ, options::OPT_fno_init_global_zero, + 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/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def index 8135704971aa4..3e2bd9afe7ce8 100644 --- a/flang/include/flang/Lower/LoweringOptions.def +++ b/flang/include/flang/Lower/LoweringOptions.def @@ -74,5 +74,11 @@ ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0) /// If false, lower to the complex dialect of MLIR. /// On by default. ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1) + +/// Initialization for logical type +/// -1 : No initialization +/// 0 : Initialized to .FALSE. +/// 1 : Initialized to .TRUE. +ENUM_LOWERINGOPT(LogicalInit, signed, 2, -1) #undef LOWERINGOPT #undef ENUM_LOWERINGOPT diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index f55d866435997..97fae23f0e54b 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -1541,6 +1541,22 @@ bool CompilerInvocation::createFromArgs( else invoc.loweringOpts.setInitGlobalZero(false); + // -finit-logical + if (const auto *arg = + args.getLastArg(clang::driver::options::OPT_finit_logical_EQ)) { + llvm::StringRef argValue = llvm::StringRef(arg->getValue()); + if (argValue.lower() == "true") + invoc.loweringOpts.setLogicalInit(1); + else if (argValue.lower() == "false") + invoc.loweringOpts.setLogicalInit(0); + else { + const unsigned diagID = diags.getCustomDiagID( + clang::DiagnosticsEngine::Error, + "Invalid argument to -finit-logical. Must be "); + diags.Report(diagID); + } + } + // 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/Bridge.cpp b/flang/lib/Lower/Bridge.cpp index 92aae792248c5..db66d9693b369 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -5720,6 +5720,79 @@ class FirConverter : public Fortran::lower::AbstractConverter { void instantiateVar(const Fortran::lower::pft::Variable &var, Fortran::lower::AggregateStoreMap &storeMap) { Fortran::lower::instantiateVariable(*this, var, localSymbols, storeMap); + + /// Implicit assignment is defined by the `-finit-*` family of flags. + /// These options do not initialize: + /// 1) Any variable already initialized + /// 2) objects with the POINTER attribute + /// 3) allocatable arrays + /// 4) variables that appear in an EQUIVALENCE statement + + auto isEligibleForImplicitAssignment = [&var]() -> bool { + if (!var.hasSymbol()) + return false; + + const Fortran::semantics::Symbol &sym = var.getSymbol(); + if (const auto *details = + sym.detailsIf()) { + if (details->init()) + return false; + } + + if (sym.attrs().test(Fortran::semantics::Attr::POINTER)) + return false; + + if (sym.Rank() > 0 && + sym.attrs().test(Fortran::semantics::Attr::ALLOCATABLE)) + return false; + + if (Fortran::lower::pft::getDependentVariableList(sym).size() > 1) + return false; + + return true; + }; + + auto processImplicitAssignment = [&]() -> void { + const Fortran::semantics::Symbol &sym = var.getSymbol(); + const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType(); + bool isInitLogicalFlagDefined = + (getLoweringOptions().getLogicalInit() == 1 || + getLoweringOptions().getLogicalInit() == 0); + + /* + * Process -finit-logical=true|false + * Create an implicit assignment of form `var = value`, + * where `value` is either true or false, and generically + * build the assignment. + */ + if (isInitLogicalFlagDefined && + declTy->category() == + Fortran::semantics::DeclTypeSpec::Category::Logical) { + Fortran::parser::Expr expr = + Fortran::parser::Expr{Fortran::parser::LiteralConstant{ + Fortran::parser::LogicalLiteralConstant{ + (getLoweringOptions().getLogicalInit() == 0) ? false : true, + std::optional{}}}}; + Fortran::parser::Designator designator = Fortran::parser::Designator{ + Fortran::parser::DataRef{Fortran::parser::Name{ + Fortran::parser::FindSourceLocation(sym.name()), + const_cast(&sym)}}}; + designator.source = Fortran::parser::FindSourceLocation(sym.name()); + Fortran::parser::Variable variable = Fortran::parser::Variable{ + Fortran::common::Indirection{ + std::move(designator)}}; + Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{ + std::make_tuple(std::move(variable), std::move(expr))}; + Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()}; + const Fortran::evaluate::Assignment *assign = ea.Analyze(stmt); + if (assign) + genAssignment(*assign); + } + }; + + if (isEligibleForImplicitAssignment()) + processImplicitAssignment(); + if (var.hasSymbol()) genOpenMPSymbolProperties(*this, var); } diff --git a/flang/test/Lower/logical_init.f90 b/flang/test/Lower/logical_init.f90 new file mode 100644 index 0000000000000..4fd4506496e46 --- /dev/null +++ b/flang/test/Lower/logical_init.f90 @@ -0,0 +1,82 @@ +! RUN: %flang_fc1 -emit-fir -o - %s | FileCheck --check-prefix=CHECK-UNINT %s +! RUN: %flang_fc1 -emit-fir -finit-logical=true -o - %s | FileCheck --check-prefix=CHECK-TRUE %s +! RUN: %flang_fc1 -emit-fir -finit-logical=false -o - %s | FileCheck --check-prefix=CHECK-FALSE %s + +subroutine logical_scalar +!CHECK-UNINT-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical :: x +end subroutine + + +subroutine logical_allocatable +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4> + +!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical, allocatable :: x +end subroutine + + +subroutine logical_array +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4> + +!CHECK-TRUE: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical :: x(5) +end subroutine + + +subroutine logical_pointer +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4> + +!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical, pointer :: x +end subroutine + + +subroutine logical_allocatable_array +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4> + +!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical, allocatable :: x(:) +end subroutine + + +subroutine logical_in_equivalence +!CHECK-UNINT-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-UINIT-NOT: {{.*}} = fir.convert %true : (i1) -> !fir.logical<4> + +!CHECK-TRUE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> +!CHECK-TRUE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> + +!CHECK-FALSE-NOT: {{.*}} = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK-FALSE-NOT: {{.}} = fir.convert %true : (i1) -> !fir.logical<4> + logical :: x + real :: y + equivalence(x,y) +end subroutine