diff --git a/clang/include/clang/Driver/Options.td b/clang/include/clang/Driver/Options.td index 7ae153deb9a55..321695306e3c1 100644 --- a/clang/include/clang/Driver/Options.td +++ b/clang/include/clang/Driver/Options.td @@ -7113,7 +7113,12 @@ defm dump_parse_tree : BooleanFFlag<"dump-parse-tree">, Group; defm external_blas : BooleanFFlag<"external-blas">, Group; defm f2c : BooleanFFlag<"f2c">, Group; defm frontend_optimize : BooleanFFlag<"frontend-optimize">, Group; -defm init_local_zero : BooleanFFlag<"init-local-zero">, Group; +defm init_local_zero + : BooleanFFlag<"init-local-zero">, + Group, + Visibility<[FlangOption, FC1Option]>, + HelpText<"Initialize real/integer/character/logical/complex type " + "to zero.">; defm integer_4_integer_8 : BooleanFFlag<"integer-4-integer-8">, Group; defm max_identifier_length : BooleanFFlag<"max-identifier-length">, Group; defm module_private : BooleanFFlag<"module-private">, Group; diff --git a/clang/lib/Driver/ToolChains/Flang.cpp b/clang/lib/Driver/ToolChains/Flang.cpp index a56fa41c49d34..d6ea83800a6d8 100644 --- a/clang/lib/Driver/ToolChains/Flang.cpp +++ b/clang/lib/Driver/ToolChains/Flang.cpp @@ -225,7 +225,7 @@ void Flang::addCodegenOptions(const ArgList &Args, 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_repack_arrays, options::OPT_finit_local_zero, 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 39f197d8d35c8..a7389ad62131a 100644 --- a/flang/include/flang/Lower/LoweringOptions.def +++ b/flang/include/flang/Lower/LoweringOptions.def @@ -79,5 +79,8 @@ ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1) /// of the lowering pipeline. ENUM_LOWERINGOPT(RegisterMLIRDiagnosticsHandler, unsigned, 1, 1) +/// When true, it enables semantics for -finit-local-zero during codegen. +ENUM_LOWERINGOPT(InitLocalZeroDef, unsigned, 1, 0) + #undef LOWERINGOPT #undef ENUM_LOWERINGOPT diff --git a/flang/lib/Frontend/CompilerInvocation.cpp b/flang/lib/Frontend/CompilerInvocation.cpp index 548ca675db5ea..fa4edb35e12eb 100644 --- a/flang/lib/Frontend/CompilerInvocation.cpp +++ b/flang/lib/Frontend/CompilerInvocation.cpp @@ -1617,6 +1617,11 @@ bool CompilerInvocation::createFromArgs( else invoc.loweringOpts.setInitGlobalZero(false); + // -finit-local-zero + if (args.hasArg(clang::driver::options::OPT_finit_local_zero)) { + invoc.loweringOpts.setInitLocalZeroDef(1); + } + // 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 3b711ccbe786a..87a53ce94181f 100644 --- a/flang/lib/Lower/Bridge.cpp +++ b/flang/lib/Lower/Bridge.cpp @@ -384,6 +384,159 @@ class TypeInfoConverter { llvm::SmallSetVector seen; }; +// Helper class to encapsulate utilities related to emission of implicit +// assignments. `Implicit` here implies the assignment does not +// exist in the Fortran source, but is implicit through definition +// of one or more flagsets (like -finit-* family of flags). +// General purpose usage of these utilities outside the +// scope detailed here is discouraged, and is probably wrong. +class ImplicitAssignmentGenerator { +private: + Fortran::lower::LoweringBridge &bridge; + bool isInitLocalZeroFlagDefined; + + /* + * Generates a parser::Variable for `sym` and fills + * in the correct source location for the designator. + */ + Fortran::parser::Variable generateLHS(const Fortran::semantics::Symbol &sym) { + 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)}}; + return variable; + } + + /* + * Integer, Real, and Complex types can be initialized through + * IntLiteralConstant. Further implicit casts will ensure + * type compatiblity. Default initialization to `0`. + */ + Fortran::parser::Expr generateIntLiteralConstant(uint32_t init = 0) { + std::string val = std::to_string(init); + return Fortran::parser::Expr{ + Fortran::parser::LiteralConstant{Fortran::parser::IntLiteralConstant{ + Fortran::parser::CharBlock{val.c_str(), val.size()}, + std::optional{}}}}; + } + + /* + * Logical types can be initialized with a LogicalLiteralConstant + * set to . Defaults to `false`. + */ + Fortran::parser::Expr generateLogicalLiteralConstant(bool init = false) { + return (init == false) + ? Fortran::parser::Expr{Fortran::parser::LiteralConstant{ + Fortran::parser::LogicalLiteralConstant{ + false, std::optional{}}}} + : Fortran::parser::Expr{Fortran::parser::LiteralConstant{ + Fortran::parser::LogicalLiteralConstant{ + true, std::optional{}}}}; + } + + /* + * Character types can be initialized with a FunctionReference + * to `achar(init)`. Defaults to `achar(0)`. + */ + Fortran::parser::Expr + generateACharReference(Fortran::parser::CharBlock ¤tPosition, + const Fortran::semantics::Symbol &sym, + std::string init = "0") { + // Construct a parser::Name for `achar` + std::string funcNameStr = "achar"; + Fortran::parser::CharBlock funcCharBlock = + Fortran::parser::CharBlock{funcNameStr.c_str(), funcNameStr.size()}; + Fortran::parser::Name funcName = Fortran::parser::Name{ + Fortran::parser::CharBlock{funcNameStr.c_str(), funcNameStr.size()}}; + Fortran::semantics::Scope &scope = + bridge.getSemanticsContext().FindScope(currentPosition); + Fortran::semantics::Symbol *funcSym = scope.FindSymbol(funcCharBlock); + if (funcSym) { + funcName.symbol = std::move(funcSym); + } else { + Fortran::semantics::Symbol &symbol = + bridge.getSemanticsContext() + .FindScope(currentPosition) + .MakeSymbol( + funcCharBlock, + Fortran::semantics::Attrs{Fortran::semantics::Attr::ELEMENTAL, + Fortran::semantics::Attr::INTRINSIC, + Fortran::semantics::Attr::PURE}, + Fortran::semantics::ProcEntityDetails{}); + funcName.symbol = std::move(&symbol); + } + + // Construct a RHS expression including a FunctionReference + // to `achar(init)` + Fortran::parser::Expr intExpr = Fortran::parser::Expr{ + Fortran::parser::LiteralConstant{Fortran::parser::IntLiteralConstant{ + Fortran::parser::CharBlock{init.c_str(), init.size()}, + std::optional{}}}}; + Fortran::common::Indirection indirExpr{ + std::move(intExpr)}; + Fortran::parser::ActualArg actualArg{std::move(indirExpr)}; + + Fortran::parser::ActualArgSpec argSpec = Fortran::parser::ActualArgSpec{ + std::make_tuple(std::nullopt, std::move(actualArg))}; + std::list argSpecList; + argSpecList.push_back(std::move(argSpec)); + Fortran::parser::ProcedureDesignator procDesignator = + Fortran::parser::ProcedureDesignator{std::move(funcName)}; + Fortran::parser::Call call = Fortran::parser::Call{ + std::make_tuple(std::move(procDesignator), std::move(argSpecList))}; + Fortran::parser::FunctionReference funcRef = + Fortran::parser::FunctionReference{std::move(call)}; + funcRef.source = Fortran::parser::FindSourceLocation(sym.name()); + return Fortran::parser::Expr{std::move(funcRef)}; + } + + /* + * Utility to wrap the LHS variable `var` and RHS expression `expr` into + * an assignment. + */ + inline std::unique_ptr + makeAssignment(Fortran::parser::Variable &var, Fortran::parser::Expr expr) { + Fortran::parser::AssignmentStmt stmt = Fortran::parser::AssignmentStmt{ + std::make_tuple(std::move(var), std::move(expr))}; + return std::make_unique(std::move(stmt)); + } + +public: + ImplicitAssignmentGenerator(Fortran::lower::LoweringBridge &bridge, + bool isInitLocalZeroFlagDefined) + : bridge(bridge), isInitLocalZeroFlagDefined(isInitLocalZeroFlagDefined) { + } + + std::unique_ptr + emitAssignment(Fortran::parser::CharBlock ¤tPosition, + const Fortran::semantics::Symbol &sym) { + if (isInitLocalZeroFlagDefined) { + Fortran::parser::Variable var = generateLHS(sym); + const Fortran::semantics::DeclTypeSpec *declTy = sym.GetType(); + + if (declTy->IsNumeric(Fortran::semantics::TypeCategory::Integer) || + declTy->IsNumeric(Fortran::semantics::TypeCategory::Real) || + declTy->IsNumeric(Fortran::semantics::TypeCategory::Complex)) + return makeAssignment(var, generateIntLiteralConstant()); + + else if (declTy->category() == + Fortran::semantics::DeclTypeSpec::Category::Logical) + return makeAssignment(var, generateLogicalLiteralConstant()); + + else if (declTy->category() == + Fortran::semantics::DeclTypeSpec::Category::Character) + return makeAssignment(var, + generateACharReference(currentPosition, sym)); + } + + return nullptr; + } +}; + using IncrementLoopNestInfo = llvm::SmallVector; } // namespace @@ -5877,6 +6030,57 @@ 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; + }; + + if (isEligibleForImplicitAssignment()) { + // Internal state of this class holds only the -finit-* flagsets. Hence + // can be reused for different symbols. Also minimizes the number of + // calls to `getLoweringOptions()`. + static ImplicitAssignmentGenerator implicitAssignmentGenerator{ + bridge, + /*isInitLocalZeroFlagDefined=*/getLoweringOptions() + .getInitLocalZeroDef() == 1}; + std::unique_ptr stmt = + implicitAssignmentGenerator.emitAssignment(currentPosition, + var.getSymbol()); + if (stmt.get()) { + Fortran::evaluate::ExpressionAnalyzer ea{bridge.getSemanticsContext()}; + const Fortran::evaluate::Assignment *assign = ea.Analyze(*stmt.get()); + if (assign) + genAssignment(*assign); + } + } + if (var.hasSymbol()) genOpenMPSymbolProperties(*this, var); } diff --git a/flang/test/Driver/finit-local-zero.f90 b/flang/test/Driver/finit-local-zero.f90 new file mode 100644 index 0000000000000..e24ea8ba2f1f0 --- /dev/null +++ b/flang/test/Driver/finit-local-zero.f90 @@ -0,0 +1,8 @@ +! Check that the driver passes through -finit-global-zero: +! RUN: %flang -### -S -finit-local-zero %s -o - 2>&1 | FileCheck %s + +! Check that the compiler accepts -finit-local-zero: +! RUN: %flang_fc1 -emit-hlfir -finit-local-zero %s -o - + + +! CHECK: "-fc1"{{.*}}"-finit-local-zero" diff --git a/flang/test/Lower/init-local-zero.f90 b/flang/test/Lower/init-local-zero.f90 new file mode 100644 index 0000000000000..2fcd0f874bfef --- /dev/null +++ b/flang/test/Lower/init-local-zero.f90 @@ -0,0 +1,52 @@ +! RUN: %flang_fc1 -emit-fir -finit-local-zero -o - %s | FileCheck %s + +!CHECK: %[[const:.*]] = arith.constant 0 : i32 +!CHECK: %[[X:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFuninitialized_integerEx"} +!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] {uniq_name = "_QFuninitialized_integerEx"} : (!fir.ref) -> !fir.ref +!CHECK: fir.store %[[const]] to %[[X_DECL]] : !fir.ref +subroutine uninitialized_integer + integer :: x +end subroutine + +!CHECK: %[[const:.*]] = arith.constant 0.000000e+00 : f32 +!CHECK: %[[X:.*]] = fir.alloca f32 {bindc_name = "x", uniq_name = "_QFuninitialized_realEx"} +!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] {uniq_name = "_QFuninitialized_realEx"} : (!fir.ref) -> !fir.ref +!CHECK: fir.store %[[const]] to %[[X_DECL]] : !fir.ref +subroutine uninitialized_real + real :: x +end subroutine + +!CHECK: %false = arith.constant false +!CHECK: %[[X:.*]] = fir.alloca !fir.logical<4> {bindc_name = "x", uniq_name = "_QFuninitialized_logicalEx"} +!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] {uniq_name = "_QFuninitialized_logicalEx"} : (!fir.ref>) -> !fir.ref> +!CHECK: %[[CVT:.*]] = fir.convert %false : (i1) -> !fir.logical<4> +!CHECK: fir.store %[[CVT]] to %[[X_DECL]] : !fir.ref> +subroutine uninitialized_logical + logical :: x +end subroutine + +!CHECK: %[[const:.*]] = arith.constant 0.000000e+00 : f32 +!CHECK: %[[X:.*]] = fir.alloca complex {bindc_name = "x", uniq_name = "_QFuninitialized_complexEx"} +!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] {uniq_name = "_QFuninitialized_complexEx"} : (!fir.ref>) -> !fir.ref> +!CHECK: %[[undef:.*]] = fir.undefined complex +!CHECK: %[[REAL:.*]] = fir.insert_value %[[undef]], %[[const]], [0 : index] : (complex, f32) -> complex +!CHECK: %[[COMPLEX:.*]] = fir.insert_value %[[REAL]], %[[const]], [1 : index] : (complex, f32) -> complex +!CHECK: fir.store %[[COMPLEX]] to %[[X_DECL]] : !fir.ref> +subroutine uninitialized_complex + complex :: x +end subroutine + +!CHECK: %[[X:.*]] = fir.alloca !fir.char<1> {bindc_name = "x", uniq_name = "_QFuninitialized_characterEx"} +!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] typeparams %c1 {uniq_name = "_QFuninitialized_characterEx"} : (!fir.ref>, index) -> !fir.ref> +!CHECK: %[[ADDR:.*]] = fir.address_of(@{{.*}}) : !fir.ref> +!CHECK: %[[FUNC_DECL:.*]] = fir.declare %[[ADDR]] {{.*}} +!CHECK: %[[LOAD:.*]] = fir.load %[[FUNC_DECL]] +!CHECK: fir.store %[[LOAD]] to %[[X_DECL]] +subroutine uninitialized_character + character :: x +end subroutine + +!CHECK: fir.global linkonce @{{.*}} constant : !fir.char<1> { +!CHECK: %[[VAL:.*]] = fir.string_lit "\00"(1) : !fir.char<1> +!CHECK: fir.has_value %[[VAL]] : !fir.char<1> +!CHECK: }