Skip to content

Commit c990001

Browse files
[flang] Add -fcomplex-arithmetic= option and select complex division algorithm (#146641)
This patch adds an option to select the method for computing complex number division. It uses `LoweringOptions` to determine whether to lower complex division to a runtime function call or to MLIR's `complex.div`, and `CodeGenOptions` to select the computation algorithm for `complex.div`. The available option values and their corresponding algorithms are as follows: - `full`: Lower to a runtime function call. (Default behavior) - `improved`: Lower to `complex.div` and expand to Smith's algorithm. - `basic`: Lower to `complex.div` and expand to the algebraic algorithm. See also the discussion in the following discourse post: https://discourse.llvm.org/t/optimization-of-complex-number-division/83468 --------- Co-authored-by: Tarun Prabhu <tarunprabhu@gmail.com>
1 parent bccd34f commit c990001

26 files changed

+1158
-40
lines changed

clang/include/clang/Driver/CommonArgs.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,12 @@ StringRef parseMPreferVectorWidthOption(clang::DiagnosticsEngine &Diags,
283283
StringRef parseMRecipOption(clang::DiagnosticsEngine &Diags,
284284
const llvm::opt::ArgList &Args);
285285

286+
// Convert ComplexRangeKind to a string that can be passed as a frontend option.
287+
std::string complexRangeKindToStr(LangOptions::ComplexRangeKind Range);
288+
289+
// Render a frontend option corresponding to ComplexRangeKind.
290+
std::string renderComplexRangeOption(LangOptions::ComplexRangeKind Range);
291+
286292
} // end namespace tools
287293
} // end namespace driver
288294
} // end namespace clang

clang/include/clang/Driver/Options.td

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1023,12 +1023,13 @@ defm offload_uniform_block : BoolFOption<"offload-uniform-block",
10231023
BothFlags<[], [ClangOption], " that kernels are launched with uniform block sizes (default true for CUDA/HIP and false otherwise)">>;
10241024

10251025
def fcomplex_arithmetic_EQ : Joined<["-"], "fcomplex-arithmetic=">, Group<f_Group>,
1026-
Visibility<[ClangOption, CC1Option]>,
1026+
Visibility<[ClangOption, CC1Option, FlangOption, FC1Option]>,
10271027
Values<"full,improved,promoted,basic">, NormalizedValuesScope<"LangOptions">,
1028-
NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>;
1028+
NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>,
1029+
HelpText<"Controls the calculation methods of complex number multiplication and division.">;
10291030

10301031
def complex_range_EQ : Joined<["-"], "complex-range=">, Group<f_Group>,
1031-
Visibility<[CC1Option]>,
1032+
Visibility<[CC1Option, FC1Option]>,
10321033
Values<"full,improved,promoted,basic">, NormalizedValuesScope<"LangOptions">,
10331034
NormalizedValues<["CX_Full", "CX_Improved", "CX_Promoted", "CX_Basic"]>,
10341035
MarshallingInfoEnum<LangOpts<"ComplexRange">, "CX_Full">;

clang/lib/Driver/ToolChains/Clang.cpp

Lines changed: 7 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -2750,29 +2750,10 @@ static void CollectArgsForIntegratedAssembler(Compilation &C,
27502750
}
27512751
}
27522752

2753-
static std::string ComplexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
2754-
switch (Range) {
2755-
case LangOptions::ComplexRangeKind::CX_Full:
2756-
return "full";
2757-
break;
2758-
case LangOptions::ComplexRangeKind::CX_Basic:
2759-
return "basic";
2760-
break;
2761-
case LangOptions::ComplexRangeKind::CX_Improved:
2762-
return "improved";
2763-
break;
2764-
case LangOptions::ComplexRangeKind::CX_Promoted:
2765-
return "promoted";
2766-
break;
2767-
default:
2768-
return "";
2769-
}
2770-
}
2771-
27722753
static std::string ComplexArithmeticStr(LangOptions::ComplexRangeKind Range) {
27732754
return (Range == LangOptions::ComplexRangeKind::CX_None)
27742755
? ""
2775-
: "-fcomplex-arithmetic=" + ComplexRangeKindToStr(Range);
2756+
: "-fcomplex-arithmetic=" + complexRangeKindToStr(Range);
27762757
}
27772758

27782759
static void EmitComplexRangeDiag(const Driver &D, std::string str1,
@@ -2782,14 +2763,6 @@ static void EmitComplexRangeDiag(const Driver &D, std::string str1,
27822763
}
27832764
}
27842765

2785-
static std::string
2786-
RenderComplexRangeOption(LangOptions::ComplexRangeKind Range) {
2787-
std::string ComplexRangeStr = ComplexRangeKindToStr(Range);
2788-
if (!ComplexRangeStr.empty())
2789-
return "-complex-range=" + ComplexRangeStr;
2790-
return ComplexRangeStr;
2791-
}
2792-
27932766
static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
27942767
bool OFastEnabled, const ArgList &Args,
27952768
ArgStringList &CmdArgs,
@@ -2922,7 +2895,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
29222895
case options::OPT_fcx_limited_range:
29232896
if (GccRangeComplexOption.empty()) {
29242897
if (Range != LangOptions::ComplexRangeKind::CX_Basic)
2925-
EmitComplexRangeDiag(D, RenderComplexRangeOption(Range),
2898+
EmitComplexRangeDiag(D, renderComplexRangeOption(Range),
29262899
"-fcx-limited-range");
29272900
} else {
29282901
if (GccRangeComplexOption != "-fno-cx-limited-range")
@@ -2934,7 +2907,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
29342907
break;
29352908
case options::OPT_fno_cx_limited_range:
29362909
if (GccRangeComplexOption.empty()) {
2937-
EmitComplexRangeDiag(D, RenderComplexRangeOption(Range),
2910+
EmitComplexRangeDiag(D, renderComplexRangeOption(Range),
29382911
"-fno-cx-limited-range");
29392912
} else {
29402913
if (GccRangeComplexOption != "-fcx-limited-range" &&
@@ -2948,7 +2921,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
29482921
break;
29492922
case options::OPT_fcx_fortran_rules:
29502923
if (GccRangeComplexOption.empty())
2951-
EmitComplexRangeDiag(D, RenderComplexRangeOption(Range),
2924+
EmitComplexRangeDiag(D, renderComplexRangeOption(Range),
29522925
"-fcx-fortran-rules");
29532926
else
29542927
EmitComplexRangeDiag(D, GccRangeComplexOption, "-fcx-fortran-rules");
@@ -2958,7 +2931,7 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
29582931
break;
29592932
case options::OPT_fno_cx_fortran_rules:
29602933
if (GccRangeComplexOption.empty()) {
2961-
EmitComplexRangeDiag(D, RenderComplexRangeOption(Range),
2934+
EmitComplexRangeDiag(D, renderComplexRangeOption(Range),
29622935
"-fno-cx-fortran-rules");
29632936
} else {
29642937
if (GccRangeComplexOption != "-fno-cx-limited-range")
@@ -3426,12 +3399,12 @@ static void RenderFloatingPointOptions(const ToolChain &TC, const Driver &D,
34263399
CmdArgs.push_back("-fno-strict-float-cast-overflow");
34273400

34283401
if (Range != LangOptions::ComplexRangeKind::CX_None)
3429-
ComplexRangeStr = RenderComplexRangeOption(Range);
3402+
ComplexRangeStr = renderComplexRangeOption(Range);
34303403
if (!ComplexRangeStr.empty()) {
34313404
CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr));
34323405
if (Args.hasArg(options::OPT_fcomplex_arithmetic_EQ))
34333406
CmdArgs.push_back(Args.MakeArgString("-fcomplex-arithmetic=" +
3434-
ComplexRangeKindToStr(Range)));
3407+
complexRangeKindToStr(Range)));
34353408
}
34363409
if (Args.hasArg(options::OPT_fcx_limited_range))
34373410
CmdArgs.push_back("-fcx-limited-range");

clang/lib/Driver/ToolChains/CommonArgs.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3420,3 +3420,30 @@ StringRef tools::parseMRecipOption(clang::DiagnosticsEngine &Diags,
34203420

34213421
return Out;
34223422
}
3423+
3424+
std::string tools::complexRangeKindToStr(LangOptions::ComplexRangeKind Range) {
3425+
switch (Range) {
3426+
case LangOptions::ComplexRangeKind::CX_Full:
3427+
return "full";
3428+
break;
3429+
case LangOptions::ComplexRangeKind::CX_Basic:
3430+
return "basic";
3431+
break;
3432+
case LangOptions::ComplexRangeKind::CX_Improved:
3433+
return "improved";
3434+
break;
3435+
case LangOptions::ComplexRangeKind::CX_Promoted:
3436+
return "promoted";
3437+
break;
3438+
default:
3439+
return "";
3440+
}
3441+
}
3442+
3443+
std::string
3444+
tools::renderComplexRangeOption(LangOptionsBase::ComplexRangeKind Range) {
3445+
std::string ComplexRangeStr = complexRangeKindToStr(Range);
3446+
if (!ComplexRangeStr.empty())
3447+
return "-complex-range=" + ComplexRangeStr;
3448+
return ComplexRangeStr;
3449+
}

clang/lib/Driver/ToolChains/Flang.cpp

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -612,6 +612,8 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
612612
bool AssociativeMath = false;
613613
bool ReciprocalMath = false;
614614

615+
LangOptions::ComplexRangeKind Range = LangOptions::ComplexRangeKind::CX_None;
616+
615617
if (const Arg *A = Args.getLastArg(options::OPT_ffp_contract)) {
616618
const StringRef Val = A->getValue();
617619
if (Val == "fast" || Val == "off") {
@@ -636,6 +638,20 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
636638
default:
637639
continue;
638640

641+
case options::OPT_fcomplex_arithmetic_EQ: {
642+
StringRef Val = A->getValue();
643+
if (Val == "full")
644+
Range = LangOptions::ComplexRangeKind::CX_Full;
645+
else if (Val == "improved")
646+
Range = LangOptions::ComplexRangeKind::CX_Improved;
647+
else if (Val == "basic")
648+
Range = LangOptions::ComplexRangeKind::CX_Basic;
649+
else {
650+
D.Diag(diag::err_drv_unsupported_option_argument)
651+
<< A->getSpelling() << Val;
652+
}
653+
break;
654+
}
639655
case options::OPT_fhonor_infinities:
640656
HonorINFs = true;
641657
break;
@@ -706,6 +722,13 @@ static void addFloatingPointOptions(const Driver &D, const ArgList &Args,
706722
if (!Recip.empty())
707723
CmdArgs.push_back(Args.MakeArgString("-mrecip=" + Recip));
708724

725+
if (Range != LangOptions::ComplexRangeKind::CX_None) {
726+
std::string ComplexRangeStr = renderComplexRangeOption(Range);
727+
CmdArgs.push_back(Args.MakeArgString(ComplexRangeStr));
728+
CmdArgs.push_back(Args.MakeArgString("-fcomplex-arithmetic=" +
729+
complexRangeKindToStr(Range)));
730+
}
731+
709732
if (!HonorINFs && !HonorNaNs && AssociativeMath && ReciprocalMath &&
710733
ApproxFunc && !SignedZeros &&
711734
(FPContract == "fast" || FPContract.empty())) {

flang/docs/ComplexOperations.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,4 +74,26 @@ The ComplexToStandard dialect does still call into libm for some floating
7474
point math operations, however these don't have the same ABI issues as the
7575
complex libm functions.
7676

77+
The flang driver option `-fcomplex-arithmetic=` allows you to select whether
78+
complex number division is lowered to function calls or to the `complex.div`
79+
operation in the MLIR complex dialect. To avoid the ABI issues mentioned above,
80+
the choice of function calls or the `complex.div` operation is made during the
81+
lowering phase. The behavior of this option is as follows:
82+
83+
- `basic`: Lowers to `complex.div` and is converted to algebraic formulas. No
84+
special handling to avoid overflow. NaN and infinite values are not handled.
85+
- `improved`: Lowers to `complex.div` and is converted to Smith's algorithm. See
86+
SMITH, R. L. Algorithm 116: Complex division. Commun. ACM 5, 8 (1962). This
87+
value offers improved handling for overflow in intermediate calculations, but
88+
overflow may occur. NaN and infinite values are handled.
89+
- `full`: Lowers to a call to runtime library functions. Overflow and non-finite
90+
values are handled by the library implementation. This is the default value.
91+
92+
While [the same option in clang][2] allows specifying `promoted`, this is not
93+
implemented in Flang. Also, in the case of `improved`, clang does not handle NaN
94+
and infinite values, but Flang does. These behavioral differences arise because
95+
the transformation of complex division calculations depends on the implementation
96+
of ComplexToStandard, which may change in the future.
97+
7798
[1]: https://discourse.llvm.org/t/rfc-change-lowering-of-fortran-math-intrinsics/63971
99+
[2]: https://clang.llvm.org/docs/UsersManual.html#cmdoption-fcomplex-arithmetic

flang/include/flang/Frontend/CodeGenOptions.def

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,7 @@ ENUM_CODEGENOPT(RelocationModel, llvm::Reloc::Model, 3, llvm::Reloc::PIC_) ///<
5252
ENUM_CODEGENOPT(DebugInfo, llvm::codegenoptions::DebugInfoKind, 4, llvm::codegenoptions::NoDebugInfo) ///< Level of debug info to generate
5353
ENUM_CODEGENOPT(VecLib, llvm::driver::VectorLibrary, 4, llvm::driver::VectorLibrary::NoLibrary) ///< Vector functions library to use
5454
ENUM_CODEGENOPT(FramePointer, llvm::FramePointerKind, 2, llvm::FramePointerKind::None) ///< Enable the usage of frame pointers
55+
ENUM_CODEGENOPT(ComplexRange, ComplexRangeKind, 3, ComplexRangeKind::CX_Full) ///< Method for calculating complex number division
5556

5657
ENUM_CODEGENOPT(DoConcurrentMapping, DoConcurrentMappingKind, 2, DoConcurrentMappingKind::DCMK_None) ///< Map `do concurrent` to OpenMP
5758

flang/include/flang/Frontend/CodeGenOptions.h

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -192,6 +192,31 @@ class CodeGenOptions : public CodeGenOptionsBase {
192192
return getProfileUse() == llvm::driver::ProfileCSIRInstr;
193193
}
194194

195+
/// Controls the various implementations for complex division.
196+
enum ComplexRangeKind {
197+
/// Implementation of complex division using a call to runtime library
198+
/// functions. Overflow and non-finite values are handled by the library
199+
/// implementation. This is the default value.
200+
CX_Full,
201+
202+
/// Implementation of complex division offering an improved handling
203+
/// for overflow in intermediate calculations. Overflow and non-finite
204+
/// values are handled by MLIR's implementation of "complex.div", but this
205+
/// may change in the future.
206+
CX_Improved,
207+
208+
/// Implementation of complex division using algebraic formulas at source
209+
/// precision. No special handling to avoid overflow. NaN and infinite
210+
/// values are not handled.
211+
CX_Basic,
212+
213+
/// No range rule is enabled.
214+
CX_None
215+
216+
/// TODO: Implemention of other values as needed. In Clang, "CX_Promoted"
217+
/// is implemented. (See clang/Basic/LangOptions.h)
218+
};
219+
195220
// Define accessors/mutators for code generation options of enumeration type.
196221
#define CODEGENOPT(Name, Bits, Default)
197222
#define ENUM_CODEGENOPT(Name, Type, Bits, Default) \

flang/include/flang/Lower/LoweringOptions.def

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,5 +70,9 @@ ENUM_LOWERINGOPT(CUDARuntimeCheck, unsigned, 1, 0)
7070
/// derived types defined in other compilation units.
7171
ENUM_LOWERINGOPT(SkipExternalRttiDefinition, unsigned, 1, 0)
7272

73+
/// If true, convert complex number division to runtime on the frontend.
74+
/// If false, lower to the complex dialect of MLIR.
75+
/// On by default.
76+
ENUM_LOWERINGOPT(ComplexDivisionToRuntime, unsigned, 1, 1)
7377
#undef LOWERINGOPT
7478
#undef ENUM_LOWERINGOPT

flang/include/flang/Optimizer/Builder/FIRBuilder.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -609,6 +609,17 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
609609
return integerOverflowFlags;
610610
}
611611

612+
/// Set ComplexDivisionToRuntimeFlag value. If set to true, complex number
613+
/// division is lowered to a runtime function by this builder.
614+
void setComplexDivisionToRuntimeFlag(bool flag) {
615+
complexDivisionToRuntimeFlag = flag;
616+
}
617+
618+
/// Get current ComplexDivisionToRuntimeFlag value.
619+
bool getComplexDivisionToRuntimeFlag() const {
620+
return complexDivisionToRuntimeFlag;
621+
}
622+
612623
/// Dump the current function. (debug)
613624
LLVM_DUMP_METHOD void dumpFunc();
614625

@@ -673,6 +684,10 @@ class FirOpBuilder : public mlir::OpBuilder, public mlir::OpBuilder::Listener {
673684
/// mlir::arith::IntegerOverflowFlagsAttr.
674685
mlir::arith::IntegerOverflowFlags integerOverflowFlags{};
675686

687+
/// Flag to control whether complex number division is lowered to a runtime
688+
/// function or to the MLIR complex dialect.
689+
bool complexDivisionToRuntimeFlag = true;
690+
676691
/// fir::GlobalOp and func::FuncOp symbol table to speed-up
677692
/// lookups.
678693
mlir::SymbolTable *symbolTable = nullptr;

0 commit comments

Comments
 (0)