This patch changes costAndCollectOperands to use InstructionCost for
accumulated cost values.
isHighCostExpansion will return true if the cost is too high or invalid.
Differential D92238
[SCEVExpander] Migrate costAndCollectOperands to use InstructionCost. sdesmalen on Nov 27 2020, 10:06 AM. Authored by
Details This patch changes costAndCollectOperands to use InstructionCost for isHighCostExpansion will return true if the cost is too high or invalid.
Diff Detail
Event Timeline
Comment Actions I think this is conflating use-cases. Comment Actions Really, we were already doing this; we just didn't have a special cost type. We could leave the cost as an integer, but then the code would just become way more verbose (unwrapping every InstructionCost after computing them). Using the metaphor of money, a cost is a quantity of money. I think it's reasonable to subtract money from a budget. I suppose we could accumulate a running cost, and compare it with the budget that remains an integer, but that would just introduce another variable for (IMO) no good reason. Comment Actions
Comment Actions Like @ctetreau I don't see why the algorithm shouldn't be using InstructionCost. The class has enough capabilities that any invalid state now propagates quite naturally without too big changes (InstructionCost behaves mostly like a scalar int that carries extra state).
Comment Actions
Comment Actions I have come to the realisation that an abstraction around InstructionCost specific for budgeting is actually quite useful. @lebedev.ri are you happy with these changes?
Comment Actions I'd like you to reconsider adding InstructionBudget. I think this new class is just trying to paper over people's misunderstanding of how InstructionCost works. This wouldn't be a huge issue, except now we're going to have to add a million redundant operator definitions to it ("why can't I multiply budgets?") so it's going to become a maintenance burden. Comment Actions Then please fix the InstrouctionCost comparison operators to do the right thing to avoid most of the changes in this diff in the first place. Comment Actions InstructionCost does do the right thing. InstructionCost is isomorphic to Optional<int>, except in 99% of cases, you want an invalid cost to be considered greater than any other cost. Does int do the wrong thing because std::numeric_limits<int>::max() + 1 < std::numeric_limits<int>::max()? I don't think that Cost < 0 || Cost.isInvalid() is an unreasonable change to request. Especially considering all the other code that uses InstructionCost that is written much more naturally due to the total ordering. An isExceeded() free function can be added to this file to ensure it's done correctly.
Comment Actions An attempt to meet in the middle:
Instead, the check for BudgetRemaining.isValid() is now done at the end in isHighCostExpansion. If the helper function found the budget has exceeded, or if the resulting budget has been invalidated, the expansion is considered high cost. Is this a step in the right direction? Comment Actions Won't we now not stop as soon as the budget is invalidated? Comment Actions I agree that it's strictly worse in that the early return is not triggered if anything returns an invalid cost. I disagree that InstructionCost::operator< needs to be fixed or that "the new abstraction is bad". The new abstraction has a well-defined semantics that is useful in the common case of deciding which of two costs is preferable. Throughout the codebase, we decide if thingA should be done over thingB by asking if the cost of thingA is less than the cost of thingB. If one of those things has an invalid cost, then it makes sense to consider the thing with a valid cost as less than it. If they are both invalid, then it doesn't really matter which one we pick. I'm curious: what's the objection to having the budget be a negative number, and adding costs instead of subtracting? We could also accumulate costs in a separate variable and compare Cost < Budget, which will also do the right thing in the face of an invalid cost infecting the running total. In this case, Budget doesn't even need to be an InstructionCost. Comment Actions That should be no different than before, isHighCostExpansionHelper is not recursive. The function that calls it, isHighCostExpansion, will still stop immediately when the cost is invalidated.
The part of the new abstraction that I believe may require more thought is the signedness of InstructionCost. If Cost would be unsigned, the code is already expected to deal with wrapping values due to underflow and InstructionCost::operator< would work as expected. There would be no need to add additional checks for validity. Another possibility would be to return Invalid when there is an underflow, because if there is a negative cost at any point this is probably a bug. That would still mean changing the compare "Budget < 0" in this file, but there are other ways to write it such as @ctetreau's suggests. Is this something to consider changing at some point?
Personally I find this a bit counter-intuitive, because a budget is always a positive value that is subtracted from.
Yes, I quite like that suggestion. Comment Actions
There probably isn't much I can do in this patch to take away all @lebedev.ri 's concerns regarding the comparison operators, but hopefully this patch is now written in such a way that it is clear and acceptable to everyone. I'd be happy to follow this up with possible changes to the nature of InstructionCost itself, like I suggested earlier, if that is helpful. Comment Actions Aside from the change to the assert, this version looks good to me.
Comment Actions Hi @lebedev.ri, when we introduced InstructionCost, we left open the possibility of changing the class based on new insights on how costs are used in TargetTransformInfo and IR passes. In this patch you pointed out a valid concern. It is indeed unexpected to need to have to check isValid() when wanting to know if a budget has gone negative after subtracting a cost, because even though InstructionCost is signed, subtracting Invalid currently results in Invalid, not -Invalid. I had a chat with @ctetreau offline about changing InstructionCost by making the cost unsigned (i.e. a cost can only ever be positive), or possibly restricting the class's operators that it can only ever be incrementing, so that sorting Invalid after a valid cost value would no longer be strange/unexpected. I think this would address your fundamental concerns with the abstraction. There is some investigation required on what exactly the right solution would be, but I'd be happy to follow this up separately. For the use-case in this patch (SCEVExpander), I think we now have a suitable solution that makes sense and is future proof for whatever changes we may make to InstructionCost. If you have no strong objections to the current approach (separate Cost and Budget variables), are you happy to accept the patch so that we can move this forward? Comment Actions I wasn't reachable since 02.02.2021, ~back now. Let me just complain about the interface next time i use it an obviously fall into it's traps. |
the headers need sorting - clang-format this