[flang] Support -f(no-)protect-parens (#170505)

Driver/compiler option plumbing to get -f(no-)protect-parens supported
on flang. (This option was already supported in clang, so extended the
option config to enable it in flang.)

In the compiler, support it in code gen options and in lowering options.
Hooked up lowering options with the code by @alexey-bataev that turns
off reassociation transformations.

Co-authored-by: Alexey Bataev <a.bataev@outlook.com>
This commit is contained in:
Eugene Epshteyn 2026-01-23 12:21:09 -05:00 committed by GitHub
parent a521774217
commit 69eac707ed
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
11 changed files with 149 additions and 9 deletions

View File

@ -491,7 +491,7 @@ def warn_drv_deprecated_arg_ofast : Warning<
" or '-O3' to enable only conforming optimizations">,
InGroup<DeprecatedOFast>;
def warn_drv_deprecated_arg_ofast_for_flang : Warning<
"argument '-Ofast' is deprecated; use '-O3 -ffast-math -fstack-arrays' for the same behavior,"
"argument '-Ofast' is deprecated; use '-O3 -ffast-math -fstack-arrays -fno-protect-parens' for the same behavior,"
" or '-O3 -fstack-arrays' to enable only conforming optimizations">,
InGroup<DeprecatedOFast>;
def warn_drv_deprecated_custom : Warning<

View File

@ -2929,10 +2929,10 @@ defm strict_float_cast_overflow : BoolFOption<"strict-float-cast-overflow",
defm protect_parens : BoolFOption<"protect-parens",
LangOpts<"ProtectParens">, DefaultFalse,
PosFlag<SetTrue, [], [ClangOption, CLOption, CC1Option],
PosFlag<SetTrue, [], [ClangOption, CLOption, CC1Option, FlangOption, FC1Option],
"Determines whether the optimizer honors parentheses when "
"floating-point expressions are evaluated">,
NegFlag<SetFalse>>;
NegFlag<SetFalse, [], [ClangOption, CLOption, CC1Option, FlangOption, FC1Option]>>;
defm daz_ftz : SimpleMFlag<"daz-ftz",
"Globally set", "Do not globally set",
@ -7478,6 +7478,7 @@ defm save_main_program : BoolOptionWithoutMarshalling<"f", "save-main-program",
defm stack_arrays : BoolOptionWithoutMarshalling<"f", "stack-arrays",
PosFlag<SetTrue, [], [ClangOption], "Attempt to allocate array temporaries on the stack, no matter their size">,
NegFlag<SetFalse, [], [ClangOption], "Allocate array temporaries on the heap (default)">>;
defm loop_versioning : BoolOptionWithoutMarshalling<"f", "version-loops-for-stride",
PosFlag<SetTrue, [], [ClangOption], "Create unit-strided versions of loops">,
NegFlag<SetFalse, [], [ClangOption], "Do not create unit-strided loops (default)">>;

View File

@ -203,6 +203,12 @@ void Flang::addCodegenOptions(const ArgList &Args,
!stackArrays->getOption().matches(options::OPT_fno_stack_arrays))
CmdArgs.push_back("-fstack-arrays");
// -fno-protect-parens is the default for -Ofast.
if (!Args.hasFlag(options::OPT_fprotect_parens,
options::OPT_fno_protect_parens,
/*Default=*/!Args.hasArg(options::OPT_Ofast)))
CmdArgs.push_back("-fno-protect-parens");
if (Args.hasFlag(options::OPT_funsafe_cray_pointers,
options::OPT_fno_unsafe_cray_pointers, false)) {
// TODO: currently passed as MLIR option

View File

@ -40,6 +40,7 @@ CODEGENOPT(PrepareForFullLTO , 1, 0) ///< Set when -flto is enabled on the
///< compile step.
CODEGENOPT(PrepareForThinLTO , 1, 0) ///< Set when -flto=thin is enabled on the
///< compile step.
CODEGENOPT(ProtectParens, 1, 1) ///< -fprotect-parens (enable parenthesis protection)
CODEGENOPT(StackArrays, 1, 0) ///< -fstack-arrays (enable the stack-arrays pass)
CODEGENOPT(VectorizeLoop, 1, 0) ///< Enable loop vectorization.
CODEGENOPT(VectorizeSLP, 1, 0) ///< Enable SLP vectorization.

View File

@ -34,6 +34,10 @@ ENUM_LOWERINGOPT(NoPPCNativeVecElemOrder, unsigned, 1, 0)
/// On by default.
ENUM_LOWERINGOPT(Underscoring, unsigned, 1, 1)
/// If true, respect parentheses in expression evaluation.
/// On by default.
ENUM_LOWERINGOPT(ProtectParens, unsigned, 1, 1)
/// If true, assume the behavior of integer overflow is defined
/// (i.e. wraps around as two's complement). Off by default.
ENUM_LOWERINGOPT(IntegerWrapAround, unsigned, 1, 0)

View File

@ -276,6 +276,10 @@ static void parseCodeGenArgs(Fortran::frontend::CodeGenOptions &opts,
clang::options::OPT_fno_debug_pass_manager, false))
opts.DebugPassManager = 1;
if (!args.hasFlag(clang::options::OPT_fprotect_parens,
clang::options::OPT_fno_protect_parens, true))
opts.ProtectParens = 0;
if (args.hasFlag(clang::options::OPT_fstack_arrays,
clang::options::OPT_fno_stack_arrays, false))
opts.StackArrays = 1;
@ -1920,6 +1924,7 @@ void CompilerInvocation::setLoweringOptions() {
const Fortran::common::LangOptions &langOptions = getLangOpts();
loweringOpts.setIntegerWrapAround(langOptions.getSignedOverflowBehavior() ==
Fortran::common::LangOptions::SOB_Defined);
loweringOpts.setProtectParens(codegenOpts.ProtectParens);
Fortran::common::MathOptionsBase &mathOpts = loweringOpts.getMathOptions();
// TODO: when LangOptions are finalized, we can represent
// the math related options using Fortran::commmon::MathOptionsBase,

View File

@ -12,6 +12,7 @@
#include "flang/Lower/ConvertExprToHLFIR.h"
#include "flang/Evaluate/shape.h"
#include "flang/Evaluate/tools.h"
#include "flang/Lower/AbstractConverter.h"
#include "flang/Lower/Allocatable.h"
#include "flang/Lower/CallInterface.h"
@ -37,6 +38,19 @@
namespace {
// This was modelled after isParenthesizedVariable()
template <typename T>
static bool isParenthesized(const Fortran::evaluate::Expr<T> &expr) {
using ExprVariant = decltype(Fortran::evaluate::Expr<T>::u);
using Parentheses = Fortran::evaluate::Parentheses<T>;
if constexpr (Fortran::common::HasMember<Parentheses, ExprVariant>) {
return std::get_if<Parentheses>(&expr.u) != nullptr;
} else {
return Fortran::common::visit(
[&](const auto &x) { return isParenthesized(x); }, expr.u);
}
}
/// Lower Designators to HLFIR.
class HlfirDesignatorBuilder {
private:
@ -1703,12 +1717,27 @@ private:
BinaryOp<D> binaryOp;
auto left = hlfir::loadTrivialScalar(loc, builder, gen(op.left()));
auto right = hlfir::loadTrivialScalar(loc, builder, gen(op.right()));
// "A op (...)" or "(...) op A" may need to have their reassoc flag
// turned off
const bool leftIsParens = isParenthesized(op.left());
const bool rightIsParens = isParenthesized(op.right());
const bool noReassoc =
getConverter().getLoweringOptions().getProtectParens() &&
(leftIsParens || rightIsParens);
llvm::SmallVector<mlir::Value, 1> typeParams;
if constexpr (R::category == Fortran::common::TypeCategory::Character) {
binaryOp.genResultTypeParams(loc, builder, left, right, typeParams);
}
if (rank == 0)
return binaryOp.gen(loc, builder, op.derived(), left, right);
if (rank == 0) {
auto fmfBackup = builder.getFastMathFlags();
if (noReassoc)
builder.setFastMathFlags(fmfBackup &
~mlir::arith::FastMathFlags::reassoc);
auto res = binaryOp.gen(loc, builder, op.derived(), left, right);
builder.setFastMathFlags(fmfBackup);
return res;
}
// Elemental expression.
mlir::Type elementType =
@ -1722,14 +1751,19 @@ private:
assert(right.isArray() && "must have at least one array operand");
shape = hlfir::genShape(loc, builder, right);
}
auto genKernel = [&op, &left, &right, &binaryOp](
auto genKernel = [&op, &left, &right, &binaryOp, noReassoc](
mlir::Location l, fir::FirOpBuilder &b,
mlir::ValueRange oneBasedIndices) -> hlfir::Entity {
auto fmfBackup = b.getFastMathFlags();
if (noReassoc)
b.setFastMathFlags(fmfBackup & ~mlir::arith::FastMathFlags::reassoc);
auto leftElement = hlfir::getElementAt(l, b, left, oneBasedIndices);
auto rightElement = hlfir::getElementAt(l, b, right, oneBasedIndices);
auto leftVal = hlfir::loadTrivialScalar(l, b, leftElement);
auto rightVal = hlfir::loadTrivialScalar(l, b, rightElement);
return binaryOp.gen(l, b, op.derived(), leftVal, rightVal);
auto result = binaryOp.gen(l, b, op.derived(), leftVal, rightVal);
b.setFastMathFlags(fmfBackup);
return result;
};
auto iofBackup = builder.getIntegerOverflowFlags();
// nsw is never added to operations on vector subscripts

View File

@ -3,8 +3,7 @@
! Check warning message for Ofast deprecation
! RUN: %flang -Ofast -### %s -o %t 2>&1 | FileCheck %s
! CHECK: warning: argument '-Ofast' is deprecated; use '-O3 -ffast-math -fstack-arrays' for the same behavior, or '-O3
! -fstack-arrays' to enable only conforming optimizations [-Wdeprecated-ofast]
! CHECK: warning: argument '-Ofast' is deprecated; use '-O3 -ffast-math -fstack-arrays -fno-protect-parens' for the same behavior, or '-O3 -fstack-arrays' to enable only conforming optimizations [-Wdeprecated-ofast]
! -Ofast => -ffast-math -O3 -fstack-arrays
! RUN: %flang -Ofast -fsyntax-only -### %s -o %t 2>&1 \

View File

@ -0,0 +1,13 @@
! RUN: %flang -### %s -o %t 2>&1 | FileCheck %s -check-prefix=PROTECT
! RUN: %flang -fprotect-parens -### %s -o %t 2>&1 | FileCheck %s -check-prefix=PROTECT
! RUN: %flang -fno-protect-parens -### %s -o %t 2>&1 | FileCheck %s -check-prefix=NO-PROTECT
! RUN: %flang -Ofast -### %s -o %t 2>&1 | FileCheck %s -check-prefix=NO-PROTECT
! RUN: %flang -Ofast -fprotect-parens -### %s -o %t 2>&1 | FileCheck %s -check-prefix=PROTECT
! Note: -fprotect-parens is not passed to the frontend, because it's the
! default. Only -fno-protect-parens is passed to turn off the default.
! Thus, in case of PROTECT, we don't want to have any -f[no-]protect-parens
! options.
! PROTECT-NOT: "-f{{.*}}protect-parens"
! NO-PROTECT: "-fno-protect-parens"

View File

@ -0,0 +1,45 @@
! RUN: %flang_fc1 -emit-hlfir -O3 %s -o - | FileCheck %s --check-prefix=CHECK-O3
! RUN: %flang_fc1 -emit-hlfir -O3 -ffast-math %s -o - | FileCheck %s --check-prefix=CHECK-FAST
! RUN: %flang_fc1 -emit-hlfir -O3 -ffast-math -fno-protect-parens %s -o - | FileCheck %s --check-prefix=CHECK-FAST-NO-PROTECT
subroutine test_array_parens
real, dimension(10) :: a, b, c, d, e
b = 1.0
c = 2.0
d = 3.0
e = 4.0
a = b * (c * d * e)
print *, a
end subroutine
! With -O3, fastmath<contract> everywhere and protect parens
! CHECK-O3: hlfir.elemental
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! CHECK-O3: hlfir.elemental
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! CHECK-O3: hlfir.elemental
! CHECK-O3: hlfir.no_reassoc
! CHECK-O3: hlfir.elemental
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! With -O3 -ffast-math, regulare computations have fastmath<fast>, but still
! protect parens, so the last multiplication is fastmath<contract>
! CHECK-FAST: hlfir.elemental
! CHECK-FAST: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST: hlfir.elemental
! CHECK-FAST: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST: hlfir.elemental
! CHECK-FAST: hlfir.no_reassoc
! CHECK-FAST: hlfir.elemental
! CHECK-FAST: arith.mulf {{.*}} fastmath<{{.*}}contract
! With -O3 -ffast-math -fno-protect-parens, fastmath<fast> everywhere
! (don't protect parens)
! CHECK-FAST-NO-PROTECT: hlfir.elemental
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST-NO-PROTECT: hlfir.elemental
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST-NO-PROTECT: hlfir.elemental
! CHECK-FAST-NO-PROTECT: hlfir.no_reassoc
! CHECK-FAST-NO-PROTECT: hlfir.elemental
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<{{.*}}fast

View File

@ -0,0 +1,32 @@
! RUN: %flang_fc1 -emit-hlfir -O3 %s -o - | FileCheck %s --check-prefix=CHECK-O3
! RUN: %flang_fc1 -emit-hlfir -O3 -ffast-math %s -o - | FileCheck %s --check-prefix=CHECK-FAST
! RUN: %flang_fc1 -emit-hlfir -O3 -ffast-math -fno-protect-parens %s -o - | FileCheck %s --check-prefix=CHECK-FAST-NO-PROTECT
real :: a, b, c, d, e
b = 1.0
c = 2.0
d = 3.0
e = 4.0
a = b * (c * d * e)
print *, a
end
! With -O3, fastmath<contract> everywhere and protect parens
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! CHECK-O3: hlfir.no_reassoc
! CHECK-O3: arith.mulf {{.*}} fastmath<contract>
! With -O3 -ffast-math, regulare computations have fastmath<fast>, but still
! protect parens, so the last multiplication is fastmath<contract>
! CHECK-FAST: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST: hlfir.no_reassoc
! CHECK-FAST: arith.mulf {{.*}} fastmath<{{.*}}contract
! With -O3 -ffast-math -fno-protect-parens, fastmath<fast> everywhere
! (don't protect parens)
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<fast>
! CHECK-FAST-NO-PROTECT: hlfir.no_reassoc
! CHECK-FAST-NO-PROTECT: arith.mulf {{.*}} fastmath<fast>