APInt *Payload = nullptr);
static Constant *getSNaN(Type *Ty, bool Negative = false,
APInt *Payload = nullptr);
- static Constant *getNegativeZero(Type *Ty);
+ static Constant *getZero(Type *Ty, bool Negative = false);
+ static Constant *getNegativeZero(Type *Ty) { return getZero(Ty, true); }
static Constant *getInfinity(Type *Ty, bool Negative = false);
/// Return true if Ty is big enough to represent V.
/// commutative, callers can acquire the operand 1 identity constant by
/// setting AllowRHSConstant to true. For example, any shift has a zero
/// identity constant for operand 1: X shift 0 = X.
+ /// If this is a fadd/fsub operation and we don't care about signed zeros,
+ /// then setting NSZ to true returns the identity +0.0 instead of -0.0.
/// Return nullptr if the operator does not have an identity constant.
static Constant *getBinOpIdentity(unsigned Opcode, Type *Ty,
- bool AllowRHSConstant = false);
+ bool AllowRHSConstant = false,
+ bool NSZ = false);
/// Return the absorbing element for the given binary
/// operation, i.e. a constant C such that X op C = C and C op X = C for
return C;
}
-Constant *ConstantFP::getNegativeZero(Type *Ty) {
+Constant *ConstantFP::getZero(Type *Ty, bool Negative) {
const fltSemantics &Semantics = Ty->getScalarType()->getFltSemantics();
- APFloat NegZero = APFloat::getZero(Semantics, /*Negative=*/true);
+ APFloat NegZero = APFloat::getZero(Semantics, Negative);
Constant *C = get(Ty->getContext(), NegZero);
if (VectorType *VTy = dyn_cast<VectorType>(Ty))
return C;
}
-
Constant *ConstantFP::getZeroValueForNegation(Type *Ty) {
if (Ty->isFPOrFPVectorTy())
return getNegativeZero(Ty);
}
Constant *ConstantExpr::getBinOpIdentity(unsigned Opcode, Type *Ty,
- bool AllowRHSConstant) {
+ bool AllowRHSConstant, bool NSZ) {
assert(Instruction::isBinaryOp(Opcode) && "Only binops allowed");
// Commutative opcodes: it does not matter if AllowRHSConstant is set.
case Instruction::And: // X & -1 = X
return Constant::getAllOnesValue(Ty);
case Instruction::FAdd: // X + -0.0 = X
- // TODO: If the fadd has 'nsz', should we return +0.0?
- return ConstantFP::getNegativeZero(Ty);
+ return ConstantFP::getZero(Ty, !NSZ);
case Instruction::FMul: // X * 1.0 = X
return ConstantFP::get(Ty, 1.0);
default:
}
if (OpToFold) {
- Constant *C = ConstantExpr::getBinOpIdentity(TVI->getOpcode(),
- TVI->getType(), true);
+ FastMathFlags FMF;
+ if (isa<FPMathOperator>(&SI))
+ FMF = SI.getFastMathFlags();
+ Constant *C = ConstantExpr::getBinOpIdentity(
+ TVI->getOpcode(), TVI->getType(), true, FMF.noSignedZeros());
Value *OOp = TVI->getOperand(2-OpToFold);
// Avoid creating select between 2 constants unless it's selecting
// between 0, 1 and -1.
if (!isa<Constant>(OOp) ||
(OOpIsAPInt && isSelect01(C->getUniqueInteger(), *OOpC))) {
Value *NewSel = Builder.CreateSelect(SI.getCondition(), OOp, C);
+ if (isa<FPMathOperator>(&SI))
+ cast<Instruction>(NewSel)->setFastMathFlags(FMF);
NewSel->takeName(TVI);
BinaryOperator *BO = BinaryOperator::Create(TVI->getOpcode(),
FalseVal, NewSel);
}
if (OpToFold) {
- Constant *C = ConstantExpr::getBinOpIdentity(FVI->getOpcode(),
- FVI->getType(), true);
+ FastMathFlags FMF;
+ if (isa<FPMathOperator>(&SI))
+ FMF = SI.getFastMathFlags();
+ Constant *C = ConstantExpr::getBinOpIdentity(
+ FVI->getOpcode(), FVI->getType(), true, FMF.noSignedZeros());
Value *OOp = FVI->getOperand(2-OpToFold);
// Avoid creating select between 2 constants unless it's selecting
// between 0, 1 and -1.
if (!isa<Constant>(OOp) ||
(OOpIsAPInt && isSelect01(C->getUniqueInteger(), *OOpC))) {
Value *NewSel = Builder.CreateSelect(SI.getCondition(), C, OOp);
+ if (isa<FPMathOperator>(&SI))
+ cast<Instruction>(NewSel)->setFastMathFlags(FMF);
NewSel->takeName(FVI);
BinaryOperator *BO = BinaryOperator::Create(FVI->getOpcode(),
TrueVal, NewSel);
ret float %D
}
+define <4 x float> @select_nsz_fadd_v4f32(<4 x i1> %cond, <4 x float> %A, <4 x float> %B) {
+; CHECK-LABEL: @select_nsz_fadd_v4f32(
+; CHECK-NEXT: [[C:%.*]] = select nnan nsz <4 x i1> [[COND:%.*]], <4 x float> [[B:%.*]], <4 x float> zeroinitializer
+; CHECK-NEXT: [[D:%.*]] = fadd nnan nsz <4 x float> [[C]], [[A:%.*]]
+; CHECK-NEXT: ret <4 x float> [[D]]
+;
+ %C = fadd nsz nnan <4 x float> %A, %B
+ %D = select nsz nnan <4 x i1> %cond, <4 x float> %C, <4 x float> %A
+ ret <4 x float> %D
+}
+
+define <vscale x 4 x float> @select_nsz_fadd_nxv4f32(<vscale x 4 x i1> %cond, <vscale x 4 x float> %A, <vscale x 4 x float> %B) {
+; CHECK-LABEL: @select_nsz_fadd_nxv4f32(
+; CHECK-NEXT: [[C:%.*]] = select nnan nsz <vscale x 4 x i1> [[COND:%.*]], <vscale x 4 x float> [[B:%.*]], <vscale x 4 x float> zeroinitializer
+; CHECK-NEXT: [[D:%.*]] = fadd nnan nsz <vscale x 4 x float> [[C]], [[A:%.*]]
+; CHECK-NEXT: ret <vscale x 4 x float> [[D]]
+;
+ %C = fadd nsz nnan <vscale x 4 x float> %A, %B
+ %D = select nsz nnan <vscale x 4 x i1> %cond, <vscale x 4 x float> %C, <vscale x 4 x float> %A
+ ret <vscale x 4 x float> %D
+}
+
+define <vscale x 4 x float> @select_nsz_fadd_nxv4f32_swapops(<vscale x 4 x i1> %cond, <vscale x 4 x float> %A, <vscale x 4 x float> %B) {
+; CHECK-LABEL: @select_nsz_fadd_nxv4f32_swapops(
+; CHECK-NEXT: [[C:%.*]] = select fast <vscale x 4 x i1> [[COND:%.*]], <vscale x 4 x float> zeroinitializer, <vscale x 4 x float> [[B:%.*]]
+; CHECK-NEXT: [[D:%.*]] = fadd fast <vscale x 4 x float> [[C]], [[A:%.*]]
+; CHECK-NEXT: ret <vscale x 4 x float> [[D]]
+;
+ %C = fadd fast <vscale x 4 x float> %A, %B
+ %D = select fast <vscale x 4 x i1> %cond, <vscale x 4 x float> %A, <vscale x 4 x float> %C
+ ret <vscale x 4 x float> %D
+}
+
define float @select_fmul(i1 %cond, float %A, float %B) {
; CHECK-LABEL: @select_fmul(
; CHECK-NEXT: [[C:%.*]] = select i1 [[COND:%.*]], float [[B:%.*]], float 1.000000e+00
define <4 x float> @select_nsz_fsub_v4f32(<4 x i1> %cond, <4 x float> %A, <4 x float> %B) {
; CHECK-LABEL: @select_nsz_fsub_v4f32(
-; CHECK-NEXT: [[C:%.*]] = select <4 x i1> [[COND:%.*]], <4 x float> [[B:%.*]], <4 x float> zeroinitializer
+; CHECK-NEXT: [[C:%.*]] = select nsz <4 x i1> [[COND:%.*]], <4 x float> [[B:%.*]], <4 x float> zeroinitializer
; CHECK-NEXT: [[D:%.*]] = fsub <4 x float> [[A:%.*]], [[C]]
; CHECK-NEXT: ret <4 x float> [[D]]
;
define <vscale x 4 x float> @select_nsz_fsub_nxv4f32(<vscale x 4 x i1> %cond, <vscale x 4 x float> %A, <vscale x 4 x float> %B) {
; CHECK-LABEL: @select_nsz_fsub_nxv4f32(
-; CHECK-NEXT: [[C:%.*]] = select <vscale x 4 x i1> [[COND:%.*]], <vscale x 4 x float> [[B:%.*]], <vscale x 4 x float> zeroinitializer
+; CHECK-NEXT: [[C:%.*]] = select nsz <vscale x 4 x i1> [[COND:%.*]], <vscale x 4 x float> [[B:%.*]], <vscale x 4 x float> zeroinitializer
; CHECK-NEXT: [[D:%.*]] = fsub <vscale x 4 x float> [[A:%.*]], [[C]]
; CHECK-NEXT: ret <vscale x 4 x float> [[D]]
;