[flang-commits] [clang] [flang] [clang][flang] Add support for -finit-local-zero (PR #159788)
via flang-commits
flang-commits at lists.llvm.org
Tue Jul 21 06:58:16 PDT 2026
https://github.com/NimishMishra updated https://github.com/llvm/llvm-project/pull/159788
>From 80000310c58c79c9081eb64e00c40c9e4d3ffe73 Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Tue, 21 Jul 2026 19:16:00 +0530
Subject: [PATCH 1/3] Address review comments
---
clang/include/clang/Options/FlangOptions.td | 4 +-
clang/lib/Driver/ToolChains/Flang.cpp | 2 +-
flang/include/flang/Lower/LoweringOptions.def | 3 +
flang/lib/Frontend/CompilerInvocation.cpp | 5 +
flang/lib/Lower/ConvertVariable.cpp | 91 +++++++++++++++++++
flang/test/Driver/finit-local-zero.f90 | 8 ++
flang/test/Lower/init-local-zero.f90 | 66 ++++++++++++++
7 files changed, 177 insertions(+), 2 deletions(-)
create mode 100644 flang/test/Driver/finit-local-zero.f90
create mode 100644 flang/test/Lower/init-local-zero.f90
diff --git a/clang/include/clang/Options/FlangOptions.td b/clang/include/clang/Options/FlangOptions.td
index bafc063663fe2..79cabe35e9406 100644
--- a/clang/include/clang/Options/FlangOptions.td
+++ b/clang/include/clang/Options/FlangOptions.td
@@ -59,7 +59,9 @@ 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, and complex variables "
+ "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 d900037230f20..34452206ed6db 100644
--- a/clang/lib/Driver/ToolChains/Flang.cpp
+++ b/clang/lib/Driver/ToolChains/Flang.cpp
@@ -263,7 +263,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 0b02ffd5a3b22..2b75d579c021e 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -93,5 +93,8 @@ ENUM_LOWERINGOPT(PreserveUseDebugInfo, unsigned, 1, 0)
/// Portable, Extremum, ExtremeNum). Default: Legacy.
ENUM_LOWERINGOPT(FPMaxminBehavior, Fortran::common::FPMaxminBehavior, 2, 0)
+/// 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 79ad08353b64c..32f03215f3469 100644
--- a/flang/lib/Frontend/CompilerInvocation.cpp
+++ b/flang/lib/Frontend/CompilerInvocation.cpp
@@ -1684,6 +1684,11 @@ bool CompilerInvocation::createFromArgs(
else
invoc.loweringOpts.setInitGlobalZero(false);
+ // -finit-local-zero
+ if (args.hasArg(clang::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/ConvertVariable.cpp b/flang/lib/Lower/ConvertVariable.cpp
index 007b536d97098..607f40e48e133 100644
--- a/flang/lib/Lower/ConvertVariable.cpp
+++ b/flang/lib/Lower/ConvertVariable.cpp
@@ -31,6 +31,7 @@
#include "flang/Optimizer/Builder/FIRBuilder.h"
#include "flang/Optimizer/Builder/HLFIRTools.h"
#include "flang/Optimizer/Builder/IntrinsicCall.h"
+#include "flang/Optimizer/Builder/Runtime/Assign.h"
#include "flang/Optimizer/Builder/Runtime/Derived.h"
#include "flang/Optimizer/Builder/Todo.h"
#include "flang/Optimizer/Dialect/CUF/CUFOps.h"
@@ -1225,6 +1226,54 @@ getSafeRepackAttrs(Fortran::lower::AbstractConverter &converter) {
return attrs.empty() ? mlir::ArrayAttr{} : builder.getArrayAttr(attrs);
}
+// 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:
+ bool isInitLocalZeroFlagDefined;
+
+public:
+ ImplicitAssignmentGenerator(bool isInitLocalZeroFlagDefined)
+ : isInitLocalZeroFlagDefined(isInitLocalZeroFlagDefined) {}
+
+ void emitAssignment(Fortran::lower::AbstractConverter &converter,
+ mlir::Location loc, const Fortran::semantics::Symbol &sym,
+ Fortran::lower::SymMap &symMap) {
+ if (isInitLocalZeroFlagDefined) {
+ mlir::Type eleTy = hlfir::getFortranElementType(converter.genType(sym));
+ auto *builder = &converter.getFirOpBuilder();
+
+ if (mlir::isa<fir::CharacterType>(eleTy)) {
+ fir::factory::CharacterExprHelper helper{*builder, loc};
+ fir::CharacterType::KindTy kind =
+ helper.getCharacterType(eleTy).getFKind();
+ mlir::Value zeroCode =
+ builder->createIntegerConstant(loc, builder->getI32Type(), 0);
+ mlir::Value zero = helper.createSingletonFromCode(zeroCode, kind);
+ hlfir::Entity lhs{symMap.lookupSymbol(sym).getAddr()};
+ lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs);
+ mlir::Value rhsTmp = builder->createTemporary(loc, zero.getType());
+ builder->create<fir::StoreOp>(loc, zero, rhsTmp);
+ builder->create<hlfir::AssignOp>(loc, rhsTmp, lhs);
+ }
+
+ else if (fir::isa_integer(eleTy) || fir::isa_real(eleTy) ||
+ fir::isa_complex(eleTy) || mlir::isa<fir::LogicalType>(eleTy)) {
+ mlir::Value zero = fir::factory::createZeroValue(*builder, loc, eleTy);
+ hlfir::Entity lhs{symMap.lookupSymbol(sym).getAddr()};
+ lhs = hlfir::derefPointersAndAllocatables(loc, *builder, lhs);
+ mlir::Value rhsTmp = builder->createTemporary(loc, zero.getType());
+ builder->create<fir::StoreOp>(loc, zero, rhsTmp);
+ builder->create<hlfir::AssignOp>(loc, rhsTmp, lhs);
+ }
+ }
+ }
+};
+
/// 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.
@@ -1317,6 +1366,48 @@ static void instantiateLocal(Fortran::lower::AbstractConverter &converter,
Fortran::lower::genUnpackArray(*converterPtr, loc, *varDef, *sym);
});
}
+
+ /// 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{
+ /*isInitLocalZeroFlagDefined=*/converter.getLoweringOptions()
+ .getInitLocalZeroDef() == 1};
+
+ implicitAssignmentGenerator.emitAssignment(
+ converter, converter.getCurrentLocation(), var.getSymbol(), symMap);
+ }
}
//===----------------------------------------------------------------===//
diff --git a/flang/test/Driver/finit-local-zero.f90 b/flang/test/Driver/finit-local-zero.f90
new file mode 100644
index 0000000000000..7dd2612e624ad
--- /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..61a4d70ad788d
--- /dev/null
+++ b/flang/test/Lower/init-local-zero.f90
@@ -0,0 +1,66 @@
+! RUN: %flang_fc1 -emit-hlfir -finit-local-zero -o - %s | FileCheck %s
+
+
+!CHECK-LABEL: func.func @_QPuninitialized_integer() {
+!CHECK: %[[ZERO_ALLOCA:.*]] = fir.alloca i32
+!CHECK: %[[X_ALLOCA:.*]] = fir.alloca i32 {bindc_name = "x", uniq_name = "_QFuninitialized_integerEx"}
+!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_ALLOCA]] {uniq_name = "_QFuninitialized_integerEx"} : (!fir.ref<i32>) -> (!fir.ref<i32>, !fir.ref<i32>)
+!CHECK: %[[ZERO:.*]] = arith.constant 0 : i32
+!CHECK: fir.store %[[ZERO]] to %[[ZERO_ALLOCA]] : !fir.ref<i32>
+!CHECK: hlfir.assign %[[ZERO_ALLOCA]] to %[[X]]#0 : !fir.ref<i32>, !fir.ref<i32>
+subroutine uninitialized_integer
+ integer :: x
+end subroutine
+
+!CHECK-LABEL: func.func @_QPuninitialized_real() {
+!CHECK: %[[ZERO_ALLOCA:.*]] = fir.alloca f32
+!CHECK: %[[X_ALLOCA:.*]] = fir.alloca f32 {bindc_name = "x", uniq_name = "_QFuninitialized_realEx"}
+!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_ALLOCA]] {uniq_name = "_QFuninitialized_realEx"} : (!fir.ref<f32>) -> (!fir.ref<f32>, !fir.ref<f32>)
+!CHECK: %[[ZERO:.*]] = arith.constant 0.000000e+00 : f32
+!CHECK: fir.store %[[ZERO]] to %[[ZERO_ALLOCA]] : !fir.ref<f32>
+!CHECK: hlfir.assign %[[ZERO_ALLOCA]] to %[[X]]#0 : !fir.ref<f32>, !fir.ref<f32>
+subroutine uninitialized_real
+ real :: x
+end subroutine
+
+!CHECK-LABEL: func.func @_QPuninitialized_logical() {
+!CHECK: %[[ZERO_ALLOCA:.*]] = fir.alloca !fir.logical<4>
+!CHECK: %[[X_ALLOCA:.*]] = fir.alloca !fir.logical<4> {bindc_name = "x", uniq_name = "_QFuninitialized_logicalEx"}
+!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_ALLOCA]] {uniq_name = "_QFuninitialized_logicalEx"} : (!fir.ref<!fir.logical<4>>) -> (!fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>)
+!CHECK: %false = arith.constant false
+!CHECK: %[[CVT:.*]] = fir.convert %false : (i1) -> !fir.logical<4>
+!CHECK: fir.store %[[CVT]] to %[[ZERO_ALLOCA]] : !fir.ref<!fir.logical<4>>
+!CHECK: hlfir.assign %[[ZERO_ALLOCA]] to %[[X]]#0 : !fir.ref<!fir.logical<4>>, !fir.ref<!fir.logical<4>>
+subroutine uninitialized_logical
+ logical :: x
+end subroutine
+
+
+!CHECK-LABEL: func.func @_QPuninitialized_complex() {
+!CHECK: %[[ZERO_ALLOCA:.*]] = fir.alloca complex<f32>
+!CHECK: %[[X_ALLOCA:.*]] = fir.alloca complex<f32> {bindc_name = "x", uniq_name = "_QFuninitialized_complexEx"}
+!CHECK: %[[X:.*]]:2 = hlfir.declare %[[X_ALLOCA]] {uniq_name = "_QFuninitialized_complexEx"} : (!fir.ref<complex<f32>>) -> (!fir.ref<complex<f32>>, !fir.ref<complex<f32>>)
+!CHECK: %[[CONST:.*]] = arith.constant 0.000000e+00 : f32
+!CHECK: %[[UNDEF:.*]] = fir.undefined complex<f32>
+!CHECK: %[[IDX0:.*]] = fir.insert_value %[[UNDEF]], %[[CONST]], [0 : index] : (complex<f32>, f32) -> complex<f32>
+!CHECK: %[[IDX1:.*]] = fir.insert_value %[[IDX0]], %[[CONST]], [1 : index] : (complex<f32>, f32) -> complex<f32>
+!CHECK: fir.store %[[IDX1]] to %[[ZERO_ALLOCA]] : !fir.ref<complex<f32>>
+!CHECK: hlfir.assign %[[ZERO_ALLOCA]] to %[[X]]#0 : !fir.ref<complex<f32>>, !fir.ref<complex<f32>>
+subroutine uninitialized_complex
+ complex :: x
+end subroutine
+
+!CHECK-LABEL: func.func @_QPuninitialized_character() {
+!CHECK: %[[ZERO_ALLOCA:.*]] = fir.alloca !fir.char<1>
+!CHECK: %[[ONE:.*]] = arith.constant 1 : index
+!CHECK: %[[X_ALLOCA:.*]] = fir.alloca !fir.char<1> {bindc_name = "x", uniq_name = "_QFuninitialized_characterEx"}
+!CHECK: %3:2 = hlfir.declare %[[X_ALLOCA]] typeparams %[[ONE]] {uniq_name = "_QFuninitialized_characterEx"} : (!fir.ref<!fir.char<1>>, index) -> (!fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>)
+!CHECK: %[[CONST:.*]] = arith.constant 0 : i32
+!CHECK: %[[CVT:.*]] = fir.convert %[[CONST]] : (i32) -> i8
+!CHECK: %[[UNDEF:.*]] = fir.undefined !fir.char<1>
+!CHECK: %[[VAL:.*]] = fir.insert_value %[[UNDEF]], %[[CVT]], [0 : index] : (!fir.char<1>, i8) -> !fir.char<1>
+!CHECK: fir.store %[[VAL]] to %[[ZERO_ALLOCA]] : !fir.ref<!fir.char<1>>
+!CHECK: hlfir.assign %[[ZERO_ALLOCA]] to %[[X]]#0 : !fir.ref<!fir.char<1>>, !fir.ref<!fir.char<1>>
+subroutine uninitialized_character
+ character :: x
+end subroutine
>From a5f6b3cde11ec8ee5c1ae53b7629f91b784ceede Mon Sep 17 00:00:00 2001
From: NimishMishra <neelam.nimish at gmail.com>
Date: Tue, 21 Jul 2026 19:19:51 +0530
Subject: [PATCH 2/3] fix test
---
flang/test/Driver/finit-local-zero.f90 | 5 ++---
1 file changed, 2 insertions(+), 3 deletions(-)
diff --git a/flang/test/Driver/finit-local-zero.f90 b/flang/test/Driver/finit-local-zero.f90
index 7dd2612e624ad..8cfadc9372edc 100644
--- a/flang/test/Driver/finit-local-zero.f90
+++ b/flang/test/Driver/finit-local-zero.f90
@@ -1,8 +1,7 @@
-! Check that the driver passes through -finit-global-zero:
+! Check that the driver passes through -finit-local-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 -
-
+! RUN: %flang_fc1 -emit-hlfir -finit-local-zero %s -o /dev/null
! CHECK: "-fc1"{{.*}}"-finit-local-zero"
>From 1ad6500f8025ebcb4109469a38f95a83537ce0a5 Mon Sep 17 00:00:00 2001
From: NimishMishra <42909663+NimishMishra at users.noreply.github.com>
Date: Tue, 21 Jul 2026 19:28:04 +0530
Subject: [PATCH 3/3] Update LoweringOptions.def
---
flang/include/flang/Lower/LoweringOptions.def | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/flang/include/flang/Lower/LoweringOptions.def b/flang/include/flang/Lower/LoweringOptions.def
index 2b75d579c021e..3155cf7878608 100644
--- a/flang/include/flang/Lower/LoweringOptions.def
+++ b/flang/include/flang/Lower/LoweringOptions.def
@@ -93,7 +93,7 @@ ENUM_LOWERINGOPT(PreserveUseDebugInfo, unsigned, 1, 0)
/// Portable, Extremum, ExtremeNum). Default: Legacy.
ENUM_LOWERINGOPT(FPMaxminBehavior, Fortran::common::FPMaxminBehavior, 2, 0)
-/// When true, it enables semantics for -finit-local-zero during codegen.
+/// When true, enables semantics for -finit-local-zero during codegen.
ENUM_LOWERINGOPT(InitLocalZeroDef, unsigned, 1, 0)
#undef LOWERINGOPT
More information about the flang-commits
mailing list