[flang][driver] Accelerate complex division when -ffast-math is specified (#159689)
This patch accelerates complex division by passing `-complex-range=basic` to the frontend when the `-ffast-math` option is specified. This behavior is the same as `-fcomplex-arithmetic=basic`. A warning is issued if a different value is specified for `-fcomplex-arithmetic=`. The warning conditions will be unified with clang.
This commit is contained in:
parent
89ed5255b9
commit
fd4e77cf33
@ -304,6 +304,11 @@ std::string complexRangeKindToStr(LangOptions::ComplexRangeKind Range);
|
||||
// Render a frontend option corresponding to ComplexRangeKind.
|
||||
std::string renderComplexRangeOption(LangOptions::ComplexRangeKind Range);
|
||||
|
||||
// Set the complex range and output a warning as needed.
|
||||
void setComplexRange(const Driver &D, StringRef NewOpt,
|
||||
LangOptions::ComplexRangeKind NewRange, StringRef &LastOpt,
|
||||
LangOptions::ComplexRangeKind &Range);
|
||||
|
||||
} // end namespace tools
|
||||
} // end namespace driver
|
||||
} // end namespace clang
|
||||
|
||||
@ -2723,42 +2723,6 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
|
||||
}
|
||||
}
|
||||
|
||||
static void EmitComplexRangeDiag(const Driver &D, StringRef LastOpt,
|
||||
LangOptions::ComplexRangeKind Range,
|
||||
StringRef NewOpt,
|
||||
LangOptions::ComplexRangeKind NewRange) {
|
||||
// Do not emit a warning if NewOpt overrides LastOpt in the following cases.
|
||||
//
|
||||
// | LastOpt | NewOpt |
|
||||
// |-----------------------|-----------------------|
|
||||
// | -fcx-limited-range | -fno-cx-limited-range |
|
||||
// | -fno-cx-limited-range | -fcx-limited-range |
|
||||
// | -fcx-fortran-rules | -fno-cx-fortran-rules |
|
||||
// | -fno-cx-fortran-rules | -fcx-fortran-rules |
|
||||
// | -ffast-math | -fno-fast-math |
|
||||
// | -ffp-model= | -ffast-math |
|
||||
// | -ffp-model= | -fno-fast-math |
|
||||
// | -ffp-model= | -ffp-model= |
|
||||
// | -fcomplex-arithmetic= | -fcomplex-arithmetic= |
|
||||
if (LastOpt == NewOpt || NewOpt.empty() || LastOpt.empty() ||
|
||||
(LastOpt == "-fcx-limited-range" && NewOpt == "-fno-cx-limited-range") ||
|
||||
(LastOpt == "-fno-cx-limited-range" && NewOpt == "-fcx-limited-range") ||
|
||||
(LastOpt == "-fcx-fortran-rules" && NewOpt == "-fno-cx-fortran-rules") ||
|
||||
(LastOpt == "-fno-cx-fortran-rules" && NewOpt == "-fcx-fortran-rules") ||
|
||||
(LastOpt == "-ffast-math" && NewOpt == "-fno-fast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") && NewOpt == "-ffast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") && NewOpt == "-fno-fast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") &&
|
||||
NewOpt.starts_with("-ffp-model=")) ||
|
||||
(LastOpt.starts_with("-fcomplex-arithmetic=") &&
|
||||
NewOpt.starts_with("-fcomplex-arithmetic=")))
|
||||
return;
|
||||
|
||||
D.Diag(clang::diag::warn_drv_overriding_complex_range)
|
||||
<< LastOpt << NewOpt << complexRangeKindToStr(Range)
|
||||
<< complexRangeKindToStr(NewRange);
|
||||
}
|
||||
|
||||
static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
bool OFastEnabled, const ArgList &Args,
|
||||
ArgStringList &CmdArgs,
|
||||
@ -2815,27 +2779,19 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
std::string ComplexRangeStr;
|
||||
StringRef LastComplexRangeOption;
|
||||
|
||||
auto setComplexRange = [&](StringRef NewOption,
|
||||
LangOptions::ComplexRangeKind NewRange) {
|
||||
// Warn if user overrides the previously set complex number
|
||||
// multiplication/division option.
|
||||
if (Range != LangOptions::ComplexRangeKind::CX_None && Range != NewRange)
|
||||
EmitComplexRangeDiag(D, LastComplexRangeOption, Range, NewOption,
|
||||
NewRange);
|
||||
LastComplexRangeOption = NewOption;
|
||||
Range = NewRange;
|
||||
};
|
||||
|
||||
// Lambda to set fast-math options. This is also used by -ffp-model=fast
|
||||
auto applyFastMath = [&](bool Aggressive, StringRef CallerOption) {
|
||||
if (Aggressive) {
|
||||
HonorINFs = false;
|
||||
HonorNaNs = false;
|
||||
setComplexRange(CallerOption, LangOptions::ComplexRangeKind::CX_Basic);
|
||||
setComplexRange(D, CallerOption, LangOptions::ComplexRangeKind::CX_Basic,
|
||||
LastComplexRangeOption, Range);
|
||||
} else {
|
||||
HonorINFs = true;
|
||||
HonorNaNs = true;
|
||||
setComplexRange(CallerOption, LangOptions::ComplexRangeKind::CX_Promoted);
|
||||
setComplexRange(D, CallerOption,
|
||||
LangOptions::ComplexRangeKind::CX_Promoted,
|
||||
LastComplexRangeOption, Range);
|
||||
}
|
||||
MathErrno = false;
|
||||
AssociativeMath = true;
|
||||
@ -2887,18 +2843,24 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
default: continue;
|
||||
|
||||
case options::OPT_fcx_limited_range:
|
||||
setComplexRange(A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Basic);
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Basic,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
case options::OPT_fno_cx_limited_range:
|
||||
setComplexRange(A->getSpelling(), LangOptions::ComplexRangeKind::CX_Full);
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Full,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
case options::OPT_fcx_fortran_rules:
|
||||
setComplexRange(A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Improved);
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Improved,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
case options::OPT_fno_cx_fortran_rules:
|
||||
setComplexRange(A->getSpelling(), LangOptions::ComplexRangeKind::CX_Full);
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Full,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
case options::OPT_fcomplex_arithmetic_EQ: {
|
||||
LangOptions::ComplexRangeKind RangeVal;
|
||||
@ -2916,7 +2878,8 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
<< A->getSpelling() << Val;
|
||||
break;
|
||||
}
|
||||
setComplexRange(Args.MakeArgString(A->getSpelling() + Val), RangeVal);
|
||||
setComplexRange(D, Args.MakeArgString(A->getSpelling() + Val), RangeVal,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
}
|
||||
case options::OPT_ffp_model_EQ: {
|
||||
@ -2956,8 +2919,9 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
FPModel = Val;
|
||||
FPContract = "on";
|
||||
LastFpContractOverrideOption = "-ffp-model=precise";
|
||||
setComplexRange(Args.MakeArgString(A->getSpelling() + Val),
|
||||
LangOptions::ComplexRangeKind::CX_Full);
|
||||
setComplexRange(D, Args.MakeArgString(A->getSpelling() + Val),
|
||||
LangOptions::ComplexRangeKind::CX_Full,
|
||||
LastComplexRangeOption, Range);
|
||||
} else if (Val == "strict") {
|
||||
StrictFPModel = true;
|
||||
FPExceptionBehavior = "strict";
|
||||
@ -2966,8 +2930,9 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
LastFpContractOverrideOption = "-ffp-model=strict";
|
||||
TrappingMath = true;
|
||||
RoundingFPMath = true;
|
||||
setComplexRange(Args.MakeArgString(A->getSpelling() + Val),
|
||||
LangOptions::ComplexRangeKind::CX_Full);
|
||||
setComplexRange(D, Args.MakeArgString(A->getSpelling() + Val),
|
||||
LangOptions::ComplexRangeKind::CX_Full,
|
||||
LastComplexRangeOption, Range);
|
||||
} else
|
||||
D.Diag(diag::err_drv_unsupported_option_argument)
|
||||
<< A->getSpelling() << Val;
|
||||
@ -3174,8 +3139,9 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
|
||||
SignedZeros = true;
|
||||
restoreFPContractState();
|
||||
if (Range != LangOptions::ComplexRangeKind::CX_Full)
|
||||
setComplexRange(A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_None);
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_None,
|
||||
LastComplexRangeOption, Range);
|
||||
else
|
||||
Range = LangOptions::ComplexRangeKind::CX_None;
|
||||
LastComplexRangeOption = "";
|
||||
|
||||
@ -3557,3 +3557,51 @@ tools::renderComplexRangeOption(LangOptionsBase::ComplexRangeKind Range) {
|
||||
return "-complex-range=" + ComplexRangeStr;
|
||||
return ComplexRangeStr;
|
||||
}
|
||||
|
||||
static void emitComplexRangeDiag(const Driver &D, StringRef LastOpt,
|
||||
LangOptions::ComplexRangeKind Range,
|
||||
StringRef NewOpt,
|
||||
LangOptions::ComplexRangeKind NewRange) {
|
||||
// Do not emit a warning if NewOpt overrides LastOpt in the following cases.
|
||||
//
|
||||
// | LastOpt | NewOpt |
|
||||
// |-----------------------|-----------------------|
|
||||
// | -fcx-limited-range | -fno-cx-limited-range |
|
||||
// | -fno-cx-limited-range | -fcx-limited-range |
|
||||
// | -fcx-fortran-rules | -fno-cx-fortran-rules |
|
||||
// | -fno-cx-fortran-rules | -fcx-fortran-rules |
|
||||
// | -ffast-math | -fno-fast-math |
|
||||
// | -ffp-model= | -ffast-math |
|
||||
// | -ffp-model= | -fno-fast-math |
|
||||
// | -ffp-model= | -ffp-model= |
|
||||
// | -fcomplex-arithmetic= | -fcomplex-arithmetic= |
|
||||
if (LastOpt == NewOpt || NewOpt.empty() || LastOpt.empty() ||
|
||||
(LastOpt == "-fcx-limited-range" && NewOpt == "-fno-cx-limited-range") ||
|
||||
(LastOpt == "-fno-cx-limited-range" && NewOpt == "-fcx-limited-range") ||
|
||||
(LastOpt == "-fcx-fortran-rules" && NewOpt == "-fno-cx-fortran-rules") ||
|
||||
(LastOpt == "-fno-cx-fortran-rules" && NewOpt == "-fcx-fortran-rules") ||
|
||||
(LastOpt == "-ffast-math" && NewOpt == "-fno-fast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") && NewOpt == "-ffast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") && NewOpt == "-fno-fast-math") ||
|
||||
(LastOpt.starts_with("-ffp-model=") &&
|
||||
NewOpt.starts_with("-ffp-model=")) ||
|
||||
(LastOpt.starts_with("-fcomplex-arithmetic=") &&
|
||||
NewOpt.starts_with("-fcomplex-arithmetic=")))
|
||||
return;
|
||||
|
||||
D.Diag(clang::diag::warn_drv_overriding_complex_range)
|
||||
<< LastOpt << NewOpt << complexRangeKindToStr(Range)
|
||||
<< complexRangeKindToStr(NewRange);
|
||||
}
|
||||
|
||||
void tools::setComplexRange(const Driver &D, StringRef NewOpt,
|
||||
LangOptions::ComplexRangeKind NewRange,
|
||||
StringRef &LastOpt,
|
||||
LangOptions::ComplexRangeKind &Range) {
|
||||
// Warn if user overrides the previously set complex number
|
||||
// multiplication/division option.
|
||||
if (Range != LangOptions::ComplexRangeKind::CX_None && Range != NewRange)
|
||||
emitComplexRangeDiag(D, LastOpt, Range, NewOpt, NewRange);
|
||||
LastOpt = NewOpt;
|
||||
Range = NewRange;
|
||||
}
|
||||
|
||||
@ -693,6 +693,7 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
|
||||
bool AssociativeMath = false;
|
||||
bool ReciprocalMath = false;
|
||||
|
||||
StringRef LastComplexRangeOption;
|
||||
LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_None;
|
||||
|
||||
if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
|
||||
@ -720,17 +721,22 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
|
||||
continue;
|
||||
|
||||
case options::OPT_fcomplex_arithmetic_EQ: {
|
||||
LangOptions::ComplexRangeKind NewRange;
|
||||
StringRef Val = A->getValue();
|
||||
if (Val == "full")
|
||||
Range = LangOptions::ComplexRangeKind::CX_Full;
|
||||
NewRange = LangOptions::ComplexRangeKind::CX_Full;
|
||||
else if (Val == "improved")
|
||||
Range = LangOptions::ComplexRangeKind::CX_Improved;
|
||||
NewRange = LangOptions::ComplexRangeKind::CX_Improved;
|
||||
else if (Val == "basic")
|
||||
Range = LangOptions::ComplexRangeKind::CX_Basic;
|
||||
NewRange = LangOptions::ComplexRangeKind::CX_Basic;
|
||||
else {
|
||||
D.Diag(diag::err_drv_unsupported_option_argument)
|
||||
<< A->getSpelling() << Val;
|
||||
break;
|
||||
}
|
||||
|
||||
setComplexRange(D, Args.MakeArgString(A->getSpelling() + Val), NewRange,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
}
|
||||
case options::OPT_fhonor_infinities:
|
||||
@ -779,6 +785,9 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
|
||||
ApproxFunc = true;
|
||||
SignedZeros = false;
|
||||
FPContract = "fast";
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_Basic,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
case options::OPT_fno_fast_math:
|
||||
HonorINFs = true;
|
||||
@ -792,6 +801,9 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
|
||||
// --ffp-contract=off -fno-fast-math --> -ffp-contract=off
|
||||
if (FPContract == "fast")
|
||||
FPContract = "";
|
||||
setComplexRange(D, A->getSpelling(),
|
||||
LangOptions::ComplexRangeKind::CX_None,
|
||||
LastComplexRangeOption, Range);
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
@ -93,7 +93,9 @@ While [the same option in clang][2] allows specifying `promoted`, this is not
|
||||
implemented in Flang. Also, in the case of `improved`, clang does not handle NaN
|
||||
and infinite values, but Flang does. These behavioral differences arise because
|
||||
the transformation of complex division calculations depends on the implementation
|
||||
of ComplexToStandard, which may change in the future.
|
||||
of ComplexToStandard, which may change in the future. If you specify
|
||||
`-ffast-math`, the lowering is the same as specifiying
|
||||
`-fcomplex-arithmetic=basic`.
|
||||
|
||||
[1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971
|
||||
[2]: https://clang.llvm.org/docs/UsersManual.html#cmdoption-fcomplex-arithmetic
|
||||
|
||||
@ -573,6 +573,9 @@ documentation for more details.
|
||||
These correspond to LLVM IR Fast Math attributes:
|
||||
https://llvm.org/docs/LangRef.html#fast-math-flags
|
||||
|
||||
In addition to the above, `-ffast-math` also enables
|
||||
`-fcomplex-arithmetic=basic`.
|
||||
|
||||
When `-ffast-math` is specified, any linker steps generated by the compiler
|
||||
driver will also link to `crtfastmath.o`, which adds a static constructor
|
||||
that sets the FTZ/DAZ bits in MXCSR, affecting not only the current only the
|
||||
|
||||
@ -15,6 +15,83 @@
|
||||
! RUN: not %flang -### -fcomplex-arithmetic=foo -c %s 2>&1 \
|
||||
! RUN: | FileCheck %s --check-prefix=ERR
|
||||
|
||||
! RUN: %flang -### -ffast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck %s --check-prefix=BASIC
|
||||
|
||||
! RUN: %flang -### -fno-fast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck %s --check-prefix=RANGE
|
||||
|
||||
! RUN: %flang -### -Werror -ffast-math -fno-fast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=RANGE %s
|
||||
|
||||
! RUN: %flang -### -ffast-math -fcomplex-arithmetic=full -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=FULL,ARITH-FULL-OVERRIDING,FAST-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -ffast-math -fcomplex-arithmetic=improved -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=IMPRVD,ARITH-IMPROVED-OVERRIDING,FAST-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -Werror -ffast-math -fcomplex-arithmetic=basic -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -Werror -fno-fast-math -ffast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -Werror -fno-fast-math -fcomplex-arithmetic=full -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=FULL %s
|
||||
|
||||
! RUN: %flang -### -Werror -fno-fast-math -fcomplex-arithmetic=improved -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=IMPRVD %s
|
||||
|
||||
! RUN: %flang -### -Werror -fno-fast-math -fcomplex-arithmetic=basic -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -fcomplex-arithmetic=full -ffast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC,FAST-OVERRIDING,ARITH-FULL-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=full -fno-fast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=RANGE %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=full -fcomplex-arithmetic=improved -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=IMPRVD %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=full -fcomplex-arithmetic=basic -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -fcomplex-arithmetic=improved -ffast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC,FAST-OVERRIDING,ARITH-IMPROVED-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -fcomplex-arithmetic=improved -fno-fast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=RANGE,NOFAST-OVERRIDING,ARITH-IMPROVED-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=improved -fcomplex-arithmetic=full -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=FULL %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=improved -fcomplex-arithmetic=basic -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=basic -ffast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=BASIC %s
|
||||
|
||||
! RUN: %flang -### -fcomplex-arithmetic=basic -fno-fast-math -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=RANGE,NOFAST-OVERRIDING,ARITH-BASIC-OVERRIDDEN %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=basic -fcomplex-arithmetic=full -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=FULL %s
|
||||
|
||||
! RUN: %flang -### -Werror -fcomplex-arithmetic=basic -fcomplex-arithmetic=improved -c %s 2>&1 \
|
||||
! RUN: | FileCheck --check-prefixes=IMPRVD %s
|
||||
|
||||
|
||||
! FAST-OVERRIDING: warning: '-ffast-math' sets complex range to "basic"
|
||||
! NOFAST-OVERRIDING: warning: '-fno-fast-math' sets complex range to "none"
|
||||
! ARITH-FULL-OVERRIDING: warning: '-fcomplex-arithmetic=full' sets complex range to "full"
|
||||
! ARITH-IMPROVED-OVERRIDING: warning: '-fcomplex-arithmetic=improved' sets complex range to "improved"
|
||||
|
||||
! FAST-OVERRIDDEN: overriding the setting of "basic" that was implied by '-ffast-math' [-Woverriding-complex-range]
|
||||
! ARITH-FULL-OVERRIDDEN: overriding the setting of "full" that was implied by '-fcomplex-arithmetic=full' [-Woverriding-complex-range]
|
||||
! ARITH-IMPROVED-OVERRIDDEN: overriding the setting of "improved" that was implied by '-fcomplex-arithmetic=improved' [-Woverriding-complex-range]
|
||||
! ARITH-BASIC-OVERRIDDEN: overriding the setting of "basic" that was implied by '-fcomplex-arithmetic=basic' [-Woverriding-complex-range]
|
||||
|
||||
! RANGE-NOT: -complex-range=
|
||||
! FULL: -complex-range=full
|
||||
! IMPRVD: -complex-range=improved
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user