Regarding complex number division in clang, the options for specifying the intermediate calculation method seem to be as follows:
-fno-cx-limited-range, -fno-cx-fortran-rules : Translates to a runtime function call (default behavior)
-fcx-limited-range : Expands to a basic division calculation formula
-fcx-fortran-rules : Expands to a calculation formula that reduces the range (Smith’s method, Algorithm 116 in this paper)
-ffast-math : Expands to a basic division calculation formula
-fcomplex-arithmetic=[full|improved|promoted|basic] : Selects the calculation formula
In clang, these options seem to behave as follows for complex number division:
The last specified option is enabled.
However, -fno-fast-math does not override other options, including -ffast-math.
In other words, as shown in this example, in the current clang, if no options are specified, complex number division is translated into a call to the runtime function __divdc3, but if clang -ffast-math -fno-fast-math is used, the runtime function is expanded into expressions. Is this behavior expected?
Also, is -fno-fast-math lower in priority than other options? For example, if specified as clang -fcx-limited-range -fno-fast-math, should -fcx-limited-range be enabled, and complex number calculations be expanded into expressions instead of runtime function calls?
I think that’s definitely a bug; -fno-fast-math ought to setComplexRange(LangOptions::ComplexRangeKind::CX_Full); just like it does all the other flags, and just like -ffp-model=precise already does.
Regarding -ffast-math -fno-fast-math, I think it is be a bug.
You can know which FP optimizations are enabled by outputting frontend options passed by the Clang driver using -###. The diffence between clang -### -fsyntax-only foo.c and clang -### -fsyntax-only -ffast-math -fno-fast-math foo.c is only -complex-range=basic. I don’t think these complex options are special compared to the other options.
Regarding -fcx-limited-range -fno-fast-math, I think the current behavior is a bug, too. -fcx-limited-range should be disabled, in the same manner as -ffinite-math-only -fno-fast-math.
GCC has the same options except -fcomplex-arithmetic. You can compare Clang/GCC behavior.
Thank you for your comments! I understand that the behavior of -ffast-math -fno-fast-math is likely a bug.
Regarding the fix, I’d like to confirm the priority of the options. The priority of the options in GCC seems to be as follows:
-f[no-]cx-fortran-rules has the highest priority.
Example: When -fcx-fortran-rules -ffast-math is specified, -fcx-fortran-rules is enabled.
-fno-fast-math only disables -ffast-math.
Example: When -ffast-math -fcx-limited-range -fno-fast-math is specifiyed, -fcx-limited-range is enabled.
As you suggested, adding setComplexRange(LangOptions::ComplexRangeKind::CX_Full) when -fno-fast-math is specified is a simple implementation. However, in that case, the result of the second example would differ from GCC, and -fcx-limited-range would be disabled by -fno-fast-math. Is this behavior acceptable in clang? I believe this is acceptable because setComplexRange outputs a warning message regarding overriding.
Also, is it acceptable that the clang options -fcomplex-arithmetic= and -ffp-model= are similarly overriden by -fno-fast-math?
Generally, we’ve taken the position that for gcc options, we try to follow gcc behavior. In this case, -fcx-limited-range, -fcx-fortran-rules, and -ffast-math are all gcc options. Mixing them with clang-specific options like ffp-model and -fcomplex-arithmetic gets into poorly defined territory.
In the case of just the gcc options, this is pretty simple. -ffast-math implies -fcx-limited-range and -fno-fast-math should undo that, but if any explicit -fcx-[limited-range|fortran-rules] option has been used, that isn’t undone by -fno-fast-math.
In the case of -ffp-model the option has no negative form and it specifies a well-defined set of semantic modes, so that’s also easy.
The gcc -fcx-* options also have negative forms, and mixing them gets into ugly space. We tried to reproduce the gcc behavior, but I don’t remember exactly what that was.
As to what -fno-fast-math should do following clang-specific options, I think it makes sense to behave as set things back to the modes you’d get by default (~fp-model=precise) except -fcomplex-arithmetic should probably be preserved in the way that -fcx-fortran-rules is.
Making this work is going to be tricky, but there is a precedent to follow. Look at the restoreFPContractState handling in the RenderFloatingPointOptions function. It preserves fp-contract in a way that probably matches what we want here.
Thank you for your comment!
So, I understand that -fno-fast-math only needs to disable -ffast-math and -ffp-model=*.
For implementation, similar to fp-contract, I plan to keep the complex range as a history only when -fcx-* and -fcomplex-arithmetic=* are specified, and restore that value when -fno-fast-math is specified.
I think that’s right. It gets a little confusing if you do something like -fcx-limited-range -ffp-model=precise -fno-fast-math because, IMO, -ffp-model=precise should override -fcx-limited-range so it’s not clear which to go back to.
I just took another look at what gcc does, and it’s a bit of a mess. In gcc -ffast-math implies -fcx-limited-range but only if you haven’t previously specified -fcx-fortran-rules but gcc will happily override -fno-cx-limited-range with -ffast-math
Except for -fno-fast-math, the last specified option is enabled.
-fno-fast-mathdoes nothing regarding the configuration of complex number calculations. This is a bug.
In other words, Clang’s current behavior is not the same as GCC’s for the GCC options. Also, the relationship between the GCC and Clang options is ambiguous.
Proposal
To clarify the relationship between these options, how about the following rules, for example?
Proposed Clang Rules:
If only GCC options are specified, follow the GCC rules.
If both GCC’s enabling options and Clang-specific options are specified, the last specified option is enabled.
GCC’s disabling options only disable GCC’s enabling options. That is, they do not disable Clang-specific options.
-fno-fast-math disables the complex number calculation setting of -ffast-math and -ffp-model=fast,aggressive. That is, it does not disable -ffp-model=precise,strict, -fcomplex-arithmetic=*, or -fcx-*.
Given the rules above, Clang will behave as follows:
Even with the above rules, there are two concerns:
When both GCC and Clang-specific options are specified, the GCC options will no longer satisfy the GCC rules. For example, if -fcomplex-arithmetic=full -fcx-fortran-rules -fcx-limited-range is specified, according to the proposed Clang rule 2, the last specified option, -fcx-limited-range, will be enabled. However, if -fcx-fortran-rules -fcx-limited-range is specified, -fcx-fortran-rules will be enabled based on GCC rule 1. Would this difference be acceptable?
-fno-fast-math may cause more unstable complex number calculations. For example, if -fcx-limited-range -ffp-model=fast -fno-fast-math is specified, -fcx-limited-range implies -fcomplex-arithmetic=basic and -ffp-model=fast implies -fcomplex-arithmetic=promoted, and the latter promoted is stricter. However, based on the proposed Clang rule 4, the complex number setting of -ffp-model=fast is disabled by -fno-fast-math, basic implied by -fcx-limited-range is enabled. Is this also acceptable?
Any opinions or suggestions would be greatly appreciated. In particular, the GCC behavior is based on my understanding, so I may be wrong. Also, please comment if anything is unclear.
That’s getting way too complex! I could never figure out what a command-line was going to do with those rules…
IMHO, we should continue using last-option-wins (and just fix the bug in -fno-fast-math) for all of this, despite it being different than GCC in some edge cases.
In general, I agree with this. “Last flag wins” is something that can be understood by mere mortals and this situation is incredibly hard to reason about otherwise (it’s still hard to reason about even with “last flag wins” because of omnibus flags like -ffast-math).
That said, I think it would be nice if we could catch cases where our behavior does not match GCC’s in the edge cases and issue a GCC compatibility diagnostic. Is that a possibility?
Thank you both for your replies. I will try implementing it with “Last flag wins”.
I believe a compatibility diagnostic with GCC is possible.
“Last flag wins” behavior is incompatible with GCC when -f[no-]cx-fortran-rules is specified before -f[no-]cx-limited-range or -f[no-]fast-math. By checking the previously specified GCC option (GccRangeComplexOption), I think we can output a warning that the behavior is incompatible with GCC, in addition to the existing warning about overriding Range.
For example, I think it can be diagnosed as follows. The second warning is an example of a new diagnosis:
$ clang -fcx-fortran-rules -fcx-limited-range foo.c -###
clang: warning: overriding '-fcx-fortran-rules' option with '-fcx-limited-range' [-Woverriding-option]
clang: warning: option override regarding complex number calculations is not compatible with GCC