[PATCH] Reassociate x + -0.1234 * y into x - 0.1234 * y
Duncan P. N. Exon Smith
dexonsmith at apple.com
Mon Apr 21 13:40:53 PDT 2014
On 2014-Apr-21, at 5:21, Erik Verbruggen <erik.verbruggen at me.com> wrote:
> Hi Duncan,
>
> Thanks for the feedback! Fixed everything you brought up, and I attached a new patch.
This is getting close. I have a few more comments.
> A couple of comments inline:
>
> On 12 Apr 2014, at 22:54, Duncan P. N. Exon Smith <dexonsmith at apple.com> wrote:
>
>>
>> On 2014 Apr 12, at 07:43, Erik Verbruggen <erik.verbruggen at me.com> wrote:
>>
>>> + Value *OtherOperand = parent->getOperand(1 - OperandNr);
>>> + if (Instruction *OtherInstruction = dyn_cast<Instruction>(OtherOperand))
>>> + RedoInsts.insert(OtherInstruction);
>>
>> This is probably right, but it's not obvious to me. Why should you redo
>> the other operand?
>
> Now that the parent has changed, that other operand might be eligible for another optimisation. That said, I checked the available optimisations for fadd/fsub and I can't find any that might apply. So, I removed it in the new patch.
Thanks for the explanation. I'm happy to go either way with this.
Some comments below on your newest version.
> diff --git a/lib/Transforms/Scalar/Reassociate.cpp b/lib/Transforms/Scalar/Reassociate.cpp
> index b6b4d97..934b2e1 100644
> --- a/lib/Transforms/Scalar/Reassociate.cpp
> +++ b/lib/Transforms/Scalar/Reassociate.cpp
> @@ -192,6 +192,8 @@ namespace {
> Value *OptimizeMul(BinaryOperator *I, SmallVectorImpl<ValueEntry> &Ops);
> Value *RemoveFactorFromExpression(Value *V, Value *Factor);
> void EraseInst(Instruction *I);
> + void optimizeFAddNegExpr(ConstantFP *ConstOperand, Instruction *I,
> + int OperandNr);
> void OptimizeInst(Instruction *I);
> };
> }
> @@ -1787,6 +1789,32 @@ void Reassociate::EraseInst(Instruction *I) {
> }
> }
>
> +void Reassociate::optimizeFAddNegExpr(ConstantFP *ConstOperand, Instruction *I,
> + int OperandNr) {
> + // Change the sign of the constant.
> + APFloat Val = ConstOperand->getValueAPF();
> + Val.changeSign();
> + I->setOperand(0, ConstantFP::get(ConstOperand->getContext(), Val));
> +
> + assert(I->hasOneUse() && "Only a single use can be replaced.");
> + Instruction *Parent = I->user_back();
> +
> + assert(
> + Parent->getOpcode() == Instruction::FAdd ||
> + (Parent->getOpcode() == Instruction::FSub && Parent->getOperand(1) == I));
> + Value *OtherOperand = Parent->getOperand(1 - OperandNr);
> + BinaryOperator *NI;
> + if (Parent->getOpcode() == Instruction::FAdd)
> + NI = BinaryOperator::CreateFSub(OtherOperand, I);
> + else
> + NI = BinaryOperator::CreateFAdd(OtherOperand, I);
I find that assert hard to read. I wonder if there's a way to
reorganize the code to make it clearer? E.g., what do you think of
this?
Value *OtherOperand = Parent->getOperand(1 - OperandNr);
BinaryOperator *NI;
if (Parent->getOpcode() == Instruction::FAdd)
NI = BinaryOperator::CreateFSub(OtherOperand, I);
else {
assert(Parent->getOpcode() == Instruction::FSub);
assert(OperandNr == 1);
NI = BinaryOperator::CreateFAdd(OtherOperand, I);
}
It would be great if you came up with useful assert(... && "messages")
too.
> + NI->insertBefore(Parent);
> + NI->setName(Parent->getName() + ".repl");
> + Parent->replaceAllUsesWith(NI);
I think you're missing:
if (Parent->getParent())
Parent->eraseFromParent();
> + NI->setDebugLoc(I->getDebugLoc());
> + MadeChange = true;
> +}
> +
> /// OptimizeInst - Inspect and optimize the given instruction. Note that erasing
> /// instructions is not allowed.
> void Reassociate::OptimizeInst(Instruction *I) {
> @@ -1808,25 +1836,45 @@ void Reassociate::OptimizeInst(Instruction *I) {
> I = NI;
> }
>
> - // Floating point binary operators are not associative, but we can still
> - // commute (some) of them, to canonicalize the order of their operands.
> - // This can potentially expose more CSE opportunities, and makes writing
> - // other transformations simpler.
> if ((I->getType()->isFloatingPointTy() || I->getType()->isVectorTy())) {
> - // FAdd and FMul can be commuted.
> - if (I->getOpcode() != Instruction::FMul &&
> - I->getOpcode() != Instruction::FAdd)
> - return;
> + // Floating point binary operators are not associative, but we can still
> + // commute (some) of them, to canonicalize the order of their operands.
> + // This can potentially expose more CSE opportunities, and makes writing
> + // other transformations simpler.
>
> - Value *LHS = I->getOperand(0);
> - Value *RHS = I->getOperand(1);
> - unsigned LHSRank = getRank(LHS);
> - unsigned RHSRank = getRank(RHS);
> + // FAdd and FMul can be commuted.
> + if (I->getOpcode() == Instruction::FMul ||
> + I->getOpcode() == Instruction::FAdd) {
> + Value *LHS = I->getOperand(0);
> + Value *RHS = I->getOperand(1);
> + unsigned LHSRank = getRank(LHS);
> + unsigned RHSRank = getRank(RHS);
> +
> + // Sort the operands by rank.
> + if (RHSRank < LHSRank) {
> + I->setOperand(0, RHS);
> + I->setOperand(1, LHS);
> + }
> + }
>
> - // Sort the operands by rank.
> - if (RHSRank < LHSRank) {
> - I->setOperand(0, RHS);
> - I->setOperand(1, LHS);
> + // Transform:
> + // FAdd(x, FMul(-0.1234, y)) -> FSub(x, FMul(0.1234, y))
> + // where the FMul can also be an FDiv, and FAdd can be a FSub.
> + if (I->getOpcode() == Instruction::FMul ||
> + I->getOpcode() == Instruction::FDiv) {
> + if (ConstantFP *LHSConst = dyn_cast<ConstantFP>(I->getOperand(0))) {
> + if (LHSConst->isNegative() && I->hasOneUse()) {
> + Instruction *Parent = I->user_back();
> + if (Parent->getOpcode() == Instruction::FAdd) {
> + if (Parent->getOperand(0) == I)
> + optimizeFAddNegExpr(LHSConst, I, 0);
> + else if (Parent->getOperand(1) == I)
> + optimizeFAddNegExpr(LHSConst, I, 1);
> + } else if (Parent->getOpcode() == Instruction::FSub)
> + if (Parent->getOperand(1) == I)
> + optimizeFAddNegExpr(LHSConst, I, 1);
> + }
> + }
> }
After fixing the logic for FDiv, the nesting is out of control. Can you
reorganize? I suggest something like the following:
/// Doxygen comment about commuting?
static bool tryToCommuteFloats(Instruction *I) {
if (I->getOpcode() != Instruction::FMul &&
I->getOpcode() != Instruction::FAdd)
return false;
// ...
}
/// Doxygen comment about your change?
static bool tryToOptimizeFAddNegExpr(Instruction *I) {
if (I->getOpcode() != Instruction::FMul &&
I->getOpcode() != Instruction::FDiv)
return false;
ConstantFP *LHSConst = dyn_cast<ConstantFP>(I->getOperand(0)))
if (!LHSConst)
return false;
// ...
}
void Reassociate::OptimizeInst(Instruction *I) {
// ...
if ((I->getType()->isFloatingPointTy() || I->getType()->isVectorTy())) {
MadeChange |= tryToCommuteFloats(I);
MadeChange |= tryToOptimizeFAddNegExpr(I);
return;
}
// ...
}
If you agree, please split it into three patches.
1. The old code didn't set MadeChange = true; please fix that. I
suspect it's not an observable bug, but if you can find a testcase,
all the better.
2. Reorganize the old code with no functionality change.
3. Add your own function and call it.
I didn't think very long about those function names; maybe you'll find
better ones.
>
> return;
More information about the llvm-commits
mailing list