-
Notifications
You must be signed in to change notification settings - Fork 15k
[clang][flang] Add support for -finit-local-zero #159788
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -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.">; | ||||||||||
|
Comment on lines
+7120
to
+7121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Suggested change
|
||||||||||
| 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>; | ||||||||||
|
|
||||||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
|
|
@@ -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. | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit:
Suggested change
|
||||||
| ENUM_LOWERINGOPT(InitLocalZeroDef, unsigned, 1, 0) | ||||||
|
|
||||||
| #undef LOWERINGOPT | ||||||
| #undef ENUM_LOWERINGOPT | ||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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)) { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Braces are not necessary here. |
||
| 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. | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -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 { | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why do you need to create You could simply work at the FIR level and generate hlfir.assign. You can get the zero values using builder.createZeroValue for numerical and logical (using the element type from getFortranElementType). For characters, you can should be able to use fir::factory::CharacterExprHelper::createSingletonFromCode() There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Thanks. That could be done. Can you help me understand whether such changes will still enable us to do the following:
The reason I thought to handle assignments through There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
For I expect you may want to support snan/inf/+inf, and while there may not be existing FIR level helper to get them, I pretty sure you can easily extract the implementation to get those from ieee_value implementation in IntrinsicCall.cpp (just like
Why is hlfir.assign not covering your use case? It implements the Fortran intrinsic assignment. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay thanks, I have a clearer idea now. Let me explore this and modify this PR. |
||
| 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. | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Can you move the logic to where other cases of initializations is handled in |
||
| /// 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); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,8 @@ | ||||||
| ! Check that the driver passes through -finit-global-zero: | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
| ! 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 - | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider explicitly redirecting to
Suggested change
|
||||||
|
|
||||||
|
|
||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Could probably do without this empty line. |
||||||
| ! CHECK: "-fc1"{{.*}}"-finit-local-zero" | ||||||
| Original file line number | Diff line number | Diff line change | ||||
|---|---|---|---|---|---|---|
| @@ -0,0 +1,52 @@ | ||||||
| ! RUN: %flang_fc1 -emit-fir -finit-local-zero -o - %s | FileCheck %s | ||||||
|
|
||||||
| !CHECK: %[[const:.*]] = arith.constant 0 : i32 | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Please be consistent with the casing of pattern names. Screaming snake case is used almost exclusively in LLVM, and also in the rest of this test.
Suggested change
|
||||||
| !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> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: The name of this pattern is odd. The result is a complex, but is named real. Maybe C0 (or something else suggestive of a complex number) would be better. |
||||||
| !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(@{{.*}}) : !fir.ref<!fir.char<1>> | ||||||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it would be good to make it clear that one is taking the address of the global that is being matched starting on line 49. That would make it clear that the variable is being initialized to an empty string. |
||||||
| !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: } | ||||||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My understanding is that the
gfortran_Groupwas intended for options that were supported ingfortranbut not handled by flang. I don't think this is necessary any longer.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks a lot for the review. I am currently reworking the lowering portion of the PR; I will address these changes in the next revision of the PR.