Skip to content

Commit

Permalink
[Float2Int] Extract calcRange() method (NFC)
Browse files Browse the repository at this point in the history
This avoids the awkward "Abort" flag, because we can simply
early-return instead.
  • Loading branch information
nikic committed Mar 31, 2022
1 parent d81fa76 commit f669755
Show file tree
Hide file tree
Showing 2 changed files with 100 additions and 101 deletions.
1 change: 1 addition & 0 deletions llvm/include/llvm/Transforms/Scalar/Float2Int.h
Expand Up @@ -41,6 +41,7 @@ class Float2IntPass : public PassInfoMixin<Float2IntPass> {
ConstantRange badRange();
ConstantRange unknownRange();
ConstantRange validateRange(ConstantRange R);
ConstantRange calcRange(Instruction *I);
void walkBackwards();
void walkForwards();
bool validateAndTransform();
Expand Down
200 changes: 99 additions & 101 deletions llvm/lib/Transforms/Scalar/Float2Int.cpp
Expand Up @@ -235,116 +235,114 @@ void Float2IntPass::walkBackwards() {
}
}

// Walk forwards down the list of seen instructions, so we visit defs before
// uses.
void Float2IntPass::walkForwards() {
for (auto &It : reverse(SeenInsts)) {
if (It.second != unknownRange())
continue;
// Calculate result range from operand ranges
ConstantRange Float2IntPass::calcRange(Instruction *I) {
std::function<ConstantRange(ArrayRef<ConstantRange>)> Op;
switch (I->getOpcode()) {
// FIXME: Handle select and phi nodes.
default:
case Instruction::UIToFP:
case Instruction::SIToFP:
llvm_unreachable("Should have been handled in walkForwards!");

Instruction *I = It.first;
std::function<ConstantRange(ArrayRef<ConstantRange>)> Op;
switch (I->getOpcode()) {
// FIXME: Handle select and phi nodes.
default:
case Instruction::UIToFP:
case Instruction::SIToFP:
llvm_unreachable("Should have been handled in walkForwards!");
case Instruction::FNeg:
Op = [](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 1 && "FNeg is a unary operator!");
unsigned Size = Ops[0].getBitWidth();
auto Zero = ConstantRange(APInt::getZero(Size));
return Zero.sub(Ops[0]);
};
break;

case Instruction::FNeg:
Op = [](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 1 && "FNeg is a unary operator!");
unsigned Size = Ops[0].getBitWidth();
auto Zero = ConstantRange(APInt::getZero(Size));
return Zero.sub(Ops[0]);
};
break;
case Instruction::FAdd:
case Instruction::FSub:
case Instruction::FMul:
Op = [I](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 2 && "its a binary operator!");
auto BinOp = (Instruction::BinaryOps) I->getOpcode();
return Ops[0].binaryOp(BinOp, Ops[1]);
};
break;

case Instruction::FAdd:
case Instruction::FSub:
case Instruction::FMul:
Op = [I](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 2 && "its a binary operator!");
auto BinOp = (Instruction::BinaryOps) I->getOpcode();
return Ops[0].binaryOp(BinOp, Ops[1]);
};
break;
//
// Root-only instructions - we'll only see these if they're the
// first node in a walk.
//
case Instruction::FPToUI:
case Instruction::FPToSI:
Op = [I](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 1 && "FPTo[US]I is a unary operator!");
// Note: We're ignoring the casts output size here as that's what the
// caller expects.
auto CastOp = (Instruction::CastOps)I->getOpcode();
return Ops[0].castOp(CastOp, MaxIntegerBW+1);
};
break;

//
// Root-only instructions - we'll only see these if they're the
// first node in a walk.
//
case Instruction::FPToUI:
case Instruction::FPToSI:
Op = [I](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 1 && "FPTo[US]I is a unary operator!");
// Note: We're ignoring the casts output size here as that's what the
// caller expects.
auto CastOp = (Instruction::CastOps)I->getOpcode();
return Ops[0].castOp(CastOp, MaxIntegerBW+1);
};
break;
case Instruction::FCmp:
Op = [](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 2 && "FCmp is a binary operator!");
return Ops[0].unionWith(Ops[1]);
};
break;
}

case Instruction::FCmp:
Op = [](ArrayRef<ConstantRange> Ops) {
assert(Ops.size() == 2 && "FCmp is a binary operator!");
return Ops[0].unionWith(Ops[1]);
};
break;
SmallVector<ConstantRange, 4> OpRanges;
for (Value *O : I->operands()) {
if (Instruction *OI = dyn_cast<Instruction>(O)) {
assert(SeenInsts.find(OI) != SeenInsts.end() &&
"def not seen before use!");
OpRanges.push_back(SeenInsts.find(OI)->second);
} else if (ConstantFP *CF = dyn_cast<ConstantFP>(O)) {
// Work out if the floating point number can be losslessly represented
// as an integer.
// APFloat::convertToInteger(&Exact) purports to do what we want, but
// the exactness can be too precise. For example, negative zero can
// never be exactly converted to an integer.
//
// Instead, we ask APFloat to round itself to an integral value - this
// preserves sign-of-zero - then compare the result with the original.
//
const APFloat &F = CF->getValueAPF();

// First, weed out obviously incorrect values. Non-finite numbers
// can't be represented and neither can negative zero, unless
// we're in fast math mode.
if (!F.isFinite() ||
(F.isZero() && F.isNegative() && isa<FPMathOperator>(I) &&
!I->hasNoSignedZeros()))
return badRange();

APFloat NewF = F;
auto Res = NewF.roundToIntegral(APFloat::rmNearestTiesToEven);
if (Res != APFloat::opOK || NewF != F)
return badRange();

// OK, it's representable. Now get it.
APSInt Int(MaxIntegerBW+1, false);
bool Exact;
CF->getValueAPF().convertToInteger(Int,
APFloat::rmNearestTiesToEven,
&Exact);
OpRanges.push_back(ConstantRange(Int));
} else {
llvm_unreachable("Should have already marked this as badRange!");
}
}

bool Abort = false;
SmallVector<ConstantRange,4> OpRanges;
for (Value *O : I->operands()) {
if (Instruction *OI = dyn_cast<Instruction>(O)) {
assert(SeenInsts.find(OI) != SeenInsts.end() &&
"def not seen before use!");
OpRanges.push_back(SeenInsts.find(OI)->second);
} else if (ConstantFP *CF = dyn_cast<ConstantFP>(O)) {
// Work out if the floating point number can be losslessly represented
// as an integer.
// APFloat::convertToInteger(&Exact) purports to do what we want, but
// the exactness can be too precise. For example, negative zero can
// never be exactly converted to an integer.
//
// Instead, we ask APFloat to round itself to an integral value - this
// preserves sign-of-zero - then compare the result with the original.
//
const APFloat &F = CF->getValueAPF();

// First, weed out obviously incorrect values. Non-finite numbers
// can't be represented and neither can negative zero, unless
// we're in fast math mode.
if (!F.isFinite() ||
(F.isZero() && F.isNegative() && isa<FPMathOperator>(I) &&
!I->hasNoSignedZeros())) {
seen(I, badRange());
Abort = true;
break;
}
// Reduce the operands' ranges to a single range.
return Op(OpRanges);
}

APFloat NewF = F;
auto Res = NewF.roundToIntegral(APFloat::rmNearestTiesToEven);
if (Res != APFloat::opOK || NewF != F) {
seen(I, badRange());
Abort = true;
break;
}
// OK, it's representable. Now get it.
APSInt Int(MaxIntegerBW+1, false);
bool Exact;
CF->getValueAPF().convertToInteger(Int,
APFloat::rmNearestTiesToEven,
&Exact);
OpRanges.push_back(ConstantRange(Int));
} else {
llvm_unreachable("Should have already marked this as badRange!");
}
}
// Walk forwards down the list of seen instructions, so we visit defs before
// uses.
void Float2IntPass::walkForwards() {
for (auto &It : reverse(SeenInsts)) {
if (It.second != unknownRange())
continue;

// Reduce the operands' ranges to a single range and return.
if (!Abort)
seen(I, Op(OpRanges));
Instruction *I = It.first;
seen(I, calcRange(I));
}
}

Expand Down

0 comments on commit f669755

Please sign in to comment.