[llvm-dev] [RFC] Eliminating non-IR floating-point controls in the selection DAG
Wang, Pengfei via llvm-dev
llvm-dev at lists.llvm.org
Sat Oct 30 07:51:44 PDT 2021
I’d like to +1 for the proposal. It is annoying when doing optimization with FMF in backend due to the counterintuitive fact that target option overridden the instruction attributes.
But please take care to the existing usage in backends. Fortunately, there are not too many :)
$ grep -rwn "Options.AllowFPOpFusion" llvm/lib/Target/ |wc -l
$ grep -rwn "Options.UnsafeFPMath" llvm/lib/Target/ |wc -l
Regarding the multi front-ends compatibility issue, I have an idea.
We can write a middle-end pass and add it at the very beginning of the pipeline. The pass “translates” the IR FMFs from “non FMF flags (undetermined)” to “non FMF flags (determined)” or “some FMF flags” according to the module/function attributes. We can add a default by false option by which front ends like Clang etc. can disable it once they respect the IR FMFs strictly.
From: llvm-dev <llvm-dev-bounces at lists.llvm.org> On Behalf Of Kaylor, Andrew via llvm-dev
Sent: Saturday, October 30, 2021 4:52 AM
To: Renato Golin <rengolin at gmail.com>; llvm-dev at lists.llvm.org
Cc: Ulrich Weigand <Ulrich.Weigand at de.ibm.com>; Ballman, Aaron <aaron.ballman at intel.com>; Sebastian Pop <sebpop at gmail.com>; Ammarguellat, Zahira <zahira.ammarguellat at intel.com>; Yaxun Liu <yaxun.liu at amd.com>
Subject: Re: [llvm-dev] [RFC] Eliminating non-IR floating-point controls in the selection DAG
Thanks for the reply.
I’m not entirely clear what course of action you are suggesting. I’ll start with the two statements I didn’t understand.
> Finally, I think we need to separate the IR from DAG/MIR behaviour. It seems to me that the target option is what overrides the behaviour, not function/module options, so we should worry about the targets' behaviour, not at which level the flag is
I don’t know what you’re saying here. What do you mean by “we should worry about the targets' behaviour”? If the IR says fp-contract is not allowed for a given instruction, why should the target options be allowed to overrule that?
> There's a perfectly valid solution that has module/function/instruction flags controlling behaviour, with the most specific overriding the least specific, and none of that overridden by the target. This means we can still use the same IR flags in the same way (thus not forcing all front-ends to change) and still correct the behaviour by not making the target ignore all that.
I have a bit of an idea of what you’re getting at here, but I’d like to ask for a clarification. I think you’re saying that an instruction flag would take precedence over a function attribute, and a function attribute would take precedence over module metadata, right? The question I would have is whether we are going to view the absence of a flag/attribute/metadata as meaningful.
For example, given the rule you propose, if an instruction has the ‘reassoc’ fast-math flag set, but the function has an attribute "unsafe-fp-math"="false", then we would permit that instruction to be reassociated. But if the instruction does not have any fast-math flags set and the function has an attribute "unsafe-fp-math"="true", does the flag not being set at the instruction level take precedence over the function attribute? I suppose to maintain the current behavior it would have to. I was a bit surprised to discover that clang handles the “unsafe-fp-math” math function attribute correctly for this. If I use -ffast-math on the command line, but disable one of the fast-math flags for part of a function, it will set “unsafe-fp-math” to “false”.
In this case, clang will set TargetOptions::UnsafeFPMath to true, but SelectionDAGISel::runOnMachineFunction() calls TargetMachine::resetTargetOptions() which resets it based on the function attribute. So for this case, it all works out.
Two notes, however:
1. resetTargetOptions doesn’t handle the AllowFPOpFusion setting (as far as I can tell, there is no corresponding function attribute)
2. There is a comment for resetTargetOptions which says this:
// FIXME: This function needs to go away for a number of reasons:
// a) global state on the TargetMachine is terrible in general,
// b) these target options should be passed only on the function
// and not on the TargetMachine (via TargetOptions) at all.
As you may guess, I completely agree with the comment. FWIW, this comment was added by Eric Christopher in 2015 (https://github.com/llvm/llvm-project/commit/35a8a62125ccfa0ef804584738a295ed5c17750f).
The core of my complaint about the current state of things is that the backend is treating the fast-math flags as if they are “on or undecided” rather than “on or off.” IMO, the absence of a fast-math flag in the IR means that the behavior controlled by that flag is not permitted. That’s the way the IR is treated in IR-level optimization passes, but the backend codegen (at least in places) behaves as though the absence of a fast-math flag means “not permitted unless enabled by TargetOptions.” That’s bad as a starting point, but it’s particularly bad when you start linking together IR from multiple modules that may have been created from different compilation units with different options.
I’d also like to make one thing explicit. While the change I’m proposing would change the behavior of the backend for all front ends, it wouldn’t “break” them in the sense of producing incorrect results. When TargetOptions::AllowFPOpFusion==FPOpFusionMode::Fast the backend is *allowed* to fuse operations, but it isn’t required or guaranteed to fuse them. With the change I am proposing, the backend wouldn’t form fuse FP options unless the ‘contract’ flag were set, but that wouldn’t lead to incorrect results. It would lead to lost performance in some cases, but I think that can be recovered by generating IR with the semantics that were intended. Similarly, for the other fast-math flags. However, I will admit that it could be an uncomfortable transition for some front ends.
Regarding bloating the IR, I’m not sure if this is a theoretical problem or real one. The bitcode writer will emit a byte for the fast-math flags if any are set for each call or FP operation, and if none are set it will not emit that byte. For clang, this is a non-issue because clang always sets the fast-math flags on the individual operations. It’s at least theoretically possible that there is some front end that never sets the fast-math flags and relies on the global setting. In that case, adding the fast-math flags would bloat the IR considerably. For most of the FMF, omitting the flags would cause considerable missed optimization opportunities in the middle end, but the ‘contract’ flag is a bit of a special case because it isn’t typically used at all before codegen.
From: Renato Golin <rengolin at gmail.com<mailto:rengolin at gmail.com>>
Sent: Friday, October 29, 2021 3:00 AM
To: Kaylor, Andrew <andrew.kaylor at intel.com<mailto:andrew.kaylor at intel.com>>
Cc: llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>; Ulrich Weigand <Ulrich.Weigand at de.ibm.com<mailto:Ulrich.Weigand at de.ibm.com>>; Yaxun Liu <yaxun.liu at amd.com<mailto:yaxun.liu at amd.com>>; Ballman, Aaron <aaron.ballman at intel.com<mailto:aaron.ballman at intel.com>>; Ammarguellat, Zahira <zahira.ammarguellat at intel.com<mailto:zahira.ammarguellat at intel.com>>; Sebastian Pop <sebpop at gmail.com<mailto:sebpop at gmail.com>>
Subject: Re: [llvm-dev] [RFC] Eliminating non-IR floating-point controls in the selection DAG
IIRC, the fp-contract change was to fix some broken code but not break more. Copying Sebastian who was working on that at that time.
I agree we shouldn't have overriding behaviour flags in the back-end if the IR explicitly says what it wants. But I'd be cautious as to move all such flags to instructions.
First, this would be a behavioural change that needs the IR to change, so would affect every LLVM IR front-end, which makes it a pervasive change throughout the downstream users. So, if we decide we want to do this, we need to replace the current mess with a consistent implementation that wont break everybody else's.
Second, module/function flags can control fine-grain behaviour without bloating the IR. I don't know how the instruction flag changes the binary representation, it's probably very small anyway, but so are the module/function ones, so overall, a definite increase in size.
Finally, I think we need to separate the IR from DAG/MIR behaviour. It seems to me that the target option is what overrides the behaviour, not function/module options, so we should worry about the targets' behaviour, not at which level the flag is set.
There's a perfectly valid solution that has module/function/instruction flags controlling behaviour, with the most specific overriding the least specific, and none of that overridden by the target. This means we can still use the same IR flags in the same way (thus not forcing all front-ends to change) and still correct the behaviour by not making the target ignore all that.
Does any of that make any sense?
On Fri, 29 Oct 2021 at 01:22, Kaylor, Andrew via llvm-dev <llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>> wrote:
This is related to the recent thread about fp-contract and front end pragma controls, but I want to generalize the discussion in terms of how the target-independent codegen in the backend is implemented.
Until sometime in 2017 (I think) the fast-math flags were not passed through to the Selection DAG, and so the only ways to control floating-point behavior were through settings in the TargetOptions or by settings function attributes. Since 2017, however, the fast-math flags have been attached to floating-point nodes in the selection DAG. This leads to some ambiguous situations where the TargetOptions or function attributes can override the absence of fast-math flags on individual nodes. An example of this is the fp-contract setting. If a source file is compiled with clang using the ‘-ffp-contract=fast’ setting but the file contains either “#pragma STDC FP_CONTRACT OFF” or “#pragma clang fp contract(off)” the front end will generate IR without the ‘contract’ fast-math flag set, but the X86 backend will generate an FMA instruction anyway.
This is particularly bad in the case of CUDA, because CUDA uses fp-contract=fast by default. So, the user’s code can explicitly say “don’t generate fma here” and the compiler will respond, “meh, I think I will anyway.”
There are other cases where the backend code will check for TargetOption::UnsafeFPMath for things like reassociation that can be represented using fast-math flags.
That brings me to the RFC part of my message. I’d like to start updating the backend so that it doesn’t do things like this. As a general principle, I would say, “All semantics must be represented in the IR and the backend must respect the IR semantics.” And a corollary: “Anything which can be represented at the instruction level must be represented at the instruction level.” This corollary would eliminate potential conflicts between function attributes (like "unsafe-fp-math") and individual IR instructions.
As a first step toward this goal, I’ve prepared a patch which closes the back door for fp-contract control.
This patch is currently incomplete, in as much as I didn’t update failing tests for several target architectures. I did update the X86 and AMDGPU tests to provide examples of how they can be made to work. I will fix the rest if we decide this is the correct direction. There is a failing CUDA test in the clang front end that I think will require a different approach involving some driver changes to get clang to generate IR for the semantics it intends rather than setting an option and counting on the backend to disregard the IR.
Thanks in advance for any feedback!
LLVM Developers mailing list
llvm-dev at lists.llvm.org<mailto:llvm-dev at lists.llvm.org>
-------------- next part --------------
An HTML attachment was scrubbed...
More information about the llvm-dev