[clang] [flang] [clang][flang] Add support for -finit-local-zero (PR #159788)
via cfe-commits
cfe-commits at lists.llvm.org
Tue Oct 21 23:23:16 PDT 2025
https://github.com/NimishMishra updated https://github.com/llvm/llvm-project/pull/159788
>From de491051eb094906381cdfc895d207a1ceac6470 Mon Sep 17 00:00:00 2001
From: Nimish Mishra <neelam.nimish at gmail.com>
Date: Fri, 19 Sep 2025 20:31:58 +0530
Subject: [PATCH 1/2] [clang][flang] Add support for -finit-local-zero
---
clang/include/clang/Driver/Options.td | 7 +-
clang/lib/Driver/ToolChains/Flang.cpp | 2 +-
flang/include/flang/Lower/LoweringOptions.def | 3 +
flang/lib/Frontend/CompilerInvocation.cpp | 5 +
flang/lib/Lower/Bridge.cpp | 204 ++++++++++++++++++
flang/test/Lower/init-local-zero.f90 | 52 +++++
6 files changed, 271 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Lower/init-local-zero.f90
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<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 init_local_zero
+ : BooleanFFlag<"init-local-zero">,
+ Group<gfortran_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<gfortran_Group>;
defm max_identifier_length : BooleanFFlag<"max-identifier-length">, Group<gfortran_Group>;
defm module_private : BooleanFFlag<"module-private">, Group<gfortran_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<Fortran::semantics::SymbolRef, 32> 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<Fortran::semantics::Symbol *>(&sym)}}};
+ designator.source = Fortran::parser::FindSourceLocation(sym.name());
+ Fortran::parser::Variable variable = Fortran::parser::Variable{
+ Fortran::common::Indirection<Fortran::parser::Designator>{
+ 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<Fortran::parser::KindParam>{}}}};
+ }
+
+ /*
+ * Logical types can be initialized with a LogicalLiteralConstant
+ * set to <true/false>. 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::KindParam>{}}}}
+ : Fortran::parser::Expr{Fortran::parser::LiteralConstant{
+ Fortran::parser::LogicalLiteralConstant{
+ true, std::optional<Fortran::parser::KindParam>{}}}};
+ }
+
+ /*
+ * 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::parser::KindParam>{}}}};
+ Fortran::common::Indirection<Fortran::parser::Expr> 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<Fortran::parser::ActualArgSpec> 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<Fortran::parser::AssignmentStmt>
+ 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<Fortran::parser::AssignmentStmt>(std::move(stmt));
+ }
+
+public:
+ ImplicitAssignmentGenerator(Fortran::lower::LoweringBridge &bridge,
+ bool isInitLocalZeroFlagDefined)
+ : bridge(bridge), isInitLocalZeroFlagDefined(isInitLocalZeroFlagDefined) {
+ }
+
+ std::unique_ptr<Fortran::parser::AssignmentStmt>
+ 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<IncrementLoopInfo, 8>;
} // 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<Fortran::semantics::ObjectEntityDetails>()) {
+ 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<Fortran::parser::AssignmentStmt> 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/Lower/init-local-zero.f90 b/flang/test/Lower/init-local-zero.f90
new file mode 100644
index 0000000000000..cd19ef261c55d
--- /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<i32>) -> !fir.ref<i32>
+!CHECK: fir.store %[[const]] to %[[X_DECL]] : !fir.ref<i32>
+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<f32>) -> !fir.ref<f32>
+!CHECK: fir.store %[[const]] to %[[X_DECL]] : !fir.ref<f32>
+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.logical<4>>) -> !fir.ref<!fir.logical<4>>
+!CHECK: %[[CVT:.*]] = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK: fir.store %[[CVT]] to %[[X_DECL]] : !fir.ref<!fir.logical<4>>
+subroutine uninitialized_logical
+ logical :: x
+end subroutine
+
+!CHECK: %[[const:.*]] = arith.constant 0.000000e+00 : f32
+!CHECK: %[[X:.*]] = fir.alloca complex<f32> {bindc_name = "x", uniq_name = "_QFuninitialized_complexEx"}
+!CHECK: %[[X_DECL:.*]] = fir.declare %[[X]] {uniq_name = "_QFuninitialized_complexEx"} : (!fir.ref<complex<f32>>) -> !fir.ref<complex<f32>>
+!CHECK: %[[undef:.*]] = fir.undefined complex<f32>
+!CHECK: %[[REAL:.*]] = fir.insert_value %[[undef]], %[[const]], [0 : index] : (complex<f32>, f32) -> complex<f32>
+!CHECK: %[[COMPLEX:.*]] = fir.insert_value %[[REAL]], %[[const]], [1 : index] : (complex<f32>, f32) -> complex<f32>
+!CHECK: fir.store %[[COMPLEX]] to %[[X_DECL]] : !fir.ref<complex<f32>>
+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<!fir.char<1>>, index) -> !fir.ref<!fir.char<1>>
+!CHECK: %[[ADDR:.*]] = fir.address_of(@_QQclX00) : !fir.ref<!fir.char<1>>
+!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 @_QQclX00 constant : !fir.char<1> {
+!CHECK: %[[VAL:.*]] = fir.string_lit "\00"(1) : !fir.char<1>
+!CHECK: fir.has_value %[[VAL]] : !fir.char<1>
+!CHECK: }
>From f2ea714ea355ef797c158107c3b7fbe8e1da5ebf Mon Sep 17 00:00:00 2001
From: Nimish Mishra <neelam.nimish at gmail.com>
Date: Wed, 22 Oct 2025 11:51:24 +0530
Subject: [PATCH 2/2] Fix tests
---
flang/test/Driver/finit-local-zero.f90 | 8 ++++++++
flang/test/Lower/init-local-zero.f90 | 4 ++--
2 files changed, 10 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Driver/finit-local-zero.f90
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
index cd19ef261c55d..2fcd0f874bfef 100644
--- a/flang/test/Lower/init-local-zero.f90
+++ b/flang/test/Lower/init-local-zero.f90
@@ -38,7 +38,7 @@ subroutine uninitialized_complex
!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<!fir.char<1>>, index) -> !fir.ref<!fir.char<1>>
-!CHECK: %[[ADDR:.*]] = fir.address_of(@_QQclX00) : !fir.ref<!fir.char<1>>
+!CHECK: %[[ADDR:.*]] = fir.address_of(@{{.*}}) : !fir.ref<!fir.char<1>>
!CHECK: %[[FUNC_DECL:.*]] = fir.declare %[[ADDR]] {{.*}}
!CHECK: %[[LOAD:.*]] = fir.load %[[FUNC_DECL]]
!CHECK: fir.store %[[LOAD]] to %[[X_DECL]]
@@ -46,7 +46,7 @@ subroutine uninitialized_character
character :: x
end subroutine
-!CHECK: fir.global linkonce @_QQclX00 constant : !fir.char<1> {
+!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: }
More information about the cfe-commits
mailing list