if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
- bool LHSOK = Visit(E->getLHS());
+ // Track whether the LHS or RHS is real at the type system level. When this is
+ // the case we can simplify our evaluation strategy.
+ bool LHSReal = false, RHSReal = false;
+
+ bool LHSOK;
+ if (E->getLHS()->getType()->isRealFloatingType()) {
+ LHSReal = true;
+ APFloat &Real = Result.FloatReal;
+ LHSOK = EvaluateFloat(E->getLHS(), Real, Info);
+ if (LHSOK) {
+ Result.makeComplexFloat();
+ Result.FloatImag = APFloat(Real.getSemantics());
+ }
+ } else {
+ LHSOK = Visit(E->getLHS());
+ }
if (!LHSOK && !Info.keepEvaluatingAfterFailure())
return false;
ComplexValue RHS;
- if (!EvaluateComplex(E->getRHS(), RHS, Info) || !LHSOK)
+ if (E->getRHS()->getType()->isRealFloatingType()) {
+ RHSReal = true;
+ APFloat &Real = RHS.FloatReal;
+ if (!EvaluateFloat(E->getRHS(), Real, Info) || !LHSOK)
+ return false;
+ RHS.makeComplexFloat();
+ RHS.FloatImag = APFloat(Real.getSemantics());
+ } else if (!EvaluateComplex(E->getRHS(), RHS, Info) || !LHSOK)
return false;
- assert(Result.isComplexFloat() == RHS.isComplexFloat() &&
- "Invalid operands to binary operator.");
+ assert(!(LHSReal && RHSReal) &&
+ "Cannot have both operands of a complex operation be real.");
switch (E->getOpcode()) {
default: return Error(E);
case BO_Add:
if (Result.isComplexFloat()) {
Result.getComplexFloatReal().add(RHS.getComplexFloatReal(),
APFloat::rmNearestTiesToEven);
- Result.getComplexFloatImag().add(RHS.getComplexFloatImag(),
- APFloat::rmNearestTiesToEven);
+ if (LHSReal)
+ Result.getComplexFloatImag() = RHS.getComplexFloatImag();
+ else if (!RHSReal)
+ Result.getComplexFloatImag().add(RHS.getComplexFloatImag(),
+ APFloat::rmNearestTiesToEven);
} else {
Result.getComplexIntReal() += RHS.getComplexIntReal();
Result.getComplexIntImag() += RHS.getComplexIntImag();
if (Result.isComplexFloat()) {
Result.getComplexFloatReal().subtract(RHS.getComplexFloatReal(),
APFloat::rmNearestTiesToEven);
- Result.getComplexFloatImag().subtract(RHS.getComplexFloatImag(),
- APFloat::rmNearestTiesToEven);
+ if (LHSReal) {
+ Result.getComplexFloatImag() = RHS.getComplexFloatImag();
+ Result.getComplexFloatImag().changeSign();
+ } else if (!RHSReal) {
+ Result.getComplexFloatImag().subtract(RHS.getComplexFloatImag(),
+ APFloat::rmNearestTiesToEven);
+ }
} else {
Result.getComplexIntReal() -= RHS.getComplexIntReal();
Result.getComplexIntImag() -= RHS.getComplexIntImag();
break;
case BO_Mul:
if (Result.isComplexFloat()) {
+ // This is an implementation of complex multiplication according to the
+ // constraints laid out in C11 Annex G. The implemantion uses the
+ // following naming scheme:
+ // (a + ib) * (c + id)
ComplexValue LHS = Result;
- APFloat &LHS_r = LHS.getComplexFloatReal();
- APFloat &LHS_i = LHS.getComplexFloatImag();
- APFloat &RHS_r = RHS.getComplexFloatReal();
- APFloat &RHS_i = RHS.getComplexFloatImag();
-
- APFloat Tmp = LHS_r;
- Tmp.multiply(RHS_r, APFloat::rmNearestTiesToEven);
- Result.getComplexFloatReal() = Tmp;
- Tmp = LHS_i;
- Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven);
- Result.getComplexFloatReal().subtract(Tmp, APFloat::rmNearestTiesToEven);
-
- Tmp = LHS_r;
- Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven);
- Result.getComplexFloatImag() = Tmp;
- Tmp = LHS_i;
- Tmp.multiply(RHS_r, APFloat::rmNearestTiesToEven);
- Result.getComplexFloatImag().add(Tmp, APFloat::rmNearestTiesToEven);
+ APFloat &A = LHS.getComplexFloatReal();
+ APFloat &B = LHS.getComplexFloatImag();
+ APFloat &C = RHS.getComplexFloatReal();
+ APFloat &D = RHS.getComplexFloatImag();
+ APFloat &ResR = Result.getComplexFloatReal();
+ APFloat &ResI = Result.getComplexFloatImag();
+ if (LHSReal) {
+ assert(!RHSReal && "Cannot have two real operands for a complex op!");
+ ResR = A * C;
+ ResI = A * D;
+ } else if (RHSReal) {
+ ResR = C * A;
+ ResI = C * B;
+ } else {
+ // In the fully general case, we need to handle NaNs and infinities
+ // robustly.
+ APFloat AC = A * C;
+ APFloat BD = B * D;
+ APFloat AD = A * D;
+ APFloat BC = B * C;
+ ResR = AC - BD;
+ ResI = AD + BC;
+ if (ResR.isNaN() && ResI.isNaN()) {
+ bool Recalc = false;
+ if (A.isInfinity() || B.isInfinity()) {
+ A = APFloat::copySign(
+ APFloat(A.getSemantics(), A.isInfinity() ? 1 : 0), A);
+ B = APFloat::copySign(
+ APFloat(B.getSemantics(), B.isInfinity() ? 1 : 0), B);
+ if (C.isNaN())
+ C = APFloat::copySign(APFloat(C.getSemantics()), C);
+ if (D.isNaN())
+ D = APFloat::copySign(APFloat(D.getSemantics()), D);
+ Recalc = true;
+ }
+ if (C.isInfinity() || D.isInfinity()) {
+ C = APFloat::copySign(
+ APFloat(C.getSemantics(), C.isInfinity() ? 1 : 0), C);
+ D = APFloat::copySign(
+ APFloat(D.getSemantics(), D.isInfinity() ? 1 : 0), D);
+ if (A.isNaN())
+ A = APFloat::copySign(APFloat(A.getSemantics()), A);
+ if (B.isNaN())
+ B = APFloat::copySign(APFloat(B.getSemantics()), B);
+ Recalc = true;
+ }
+ if (!Recalc && (AC.isInfinity() || BD.isInfinity() ||
+ AD.isInfinity() || BC.isInfinity())) {
+ if (A.isNaN())
+ A = APFloat::copySign(APFloat(A.getSemantics()), A);
+ if (B.isNaN())
+ B = APFloat::copySign(APFloat(B.getSemantics()), B);
+ if (C.isNaN())
+ C = APFloat::copySign(APFloat(C.getSemantics()), C);
+ if (D.isNaN())
+ D = APFloat::copySign(APFloat(D.getSemantics()), D);
+ Recalc = true;
+ }
+ if (Recalc) {
+ ResR = APFloat::getInf(A.getSemantics()) * (A * C - B * D);
+ ResI = APFloat::getInf(A.getSemantics()) * (A * D + B * C);
+ }
+ }
+ }
} else {
ComplexValue LHS = Result;
Result.getComplexIntReal() =
break;
case BO_Div:
if (Result.isComplexFloat()) {
+ // This is an implementation of complex division according to the
+ // constraints laid out in C11 Annex G. The implemantion uses the
+ // following naming scheme:
+ // (a + ib) / (c + id)
ComplexValue LHS = Result;
- APFloat &LHS_r = LHS.getComplexFloatReal();
- APFloat &LHS_i = LHS.getComplexFloatImag();
- APFloat &RHS_r = RHS.getComplexFloatReal();
- APFloat &RHS_i = RHS.getComplexFloatImag();
- APFloat &Res_r = Result.getComplexFloatReal();
- APFloat &Res_i = Result.getComplexFloatImag();
-
- APFloat Den = RHS_r;
- Den.multiply(RHS_r, APFloat::rmNearestTiesToEven);
- APFloat Tmp = RHS_i;
- Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven);
- Den.add(Tmp, APFloat::rmNearestTiesToEven);
-
- Res_r = LHS_r;
- Res_r.multiply(RHS_r, APFloat::rmNearestTiesToEven);
- Tmp = LHS_i;
- Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven);
- Res_r.add(Tmp, APFloat::rmNearestTiesToEven);
- Res_r.divide(Den, APFloat::rmNearestTiesToEven);
-
- Res_i = LHS_i;
- Res_i.multiply(RHS_r, APFloat::rmNearestTiesToEven);
- Tmp = LHS_r;
- Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven);
- Res_i.subtract(Tmp, APFloat::rmNearestTiesToEven);
- Res_i.divide(Den, APFloat::rmNearestTiesToEven);
+ APFloat &A = LHS.getComplexFloatReal();
+ APFloat &B = LHS.getComplexFloatImag();
+ APFloat &C = RHS.getComplexFloatReal();
+ APFloat &D = RHS.getComplexFloatImag();
+ APFloat &ResR = Result.getComplexFloatReal();
+ APFloat &ResI = Result.getComplexFloatImag();
+ if (RHSReal) {
+ ResR = A / C;
+ ResI = B / C;
+ } else {
+ if (LHSReal) {
+ // No real optimizations we can do here, stub out with zero.
+ B = APFloat::getZero(A.getSemantics());
+ }
+ int DenomLogB = 0;
+ APFloat MaxCD = maxnum(abs(C), abs(D));
+ if (MaxCD.isFinite()) {
+ DenomLogB = ilogb(MaxCD);
+ C = scalbn(C, -DenomLogB);
+ D = scalbn(D, -DenomLogB);
+ }
+ APFloat Denom = C * C + D * D;
+ ResR = scalbn((A * C + B * D) / Denom, -DenomLogB);
+ ResI = scalbn((B * C - A * D) / Denom, -DenomLogB);
+ if (ResR.isNaN() && ResI.isNaN()) {
+ if (Denom.isPosZero() && (!A.isNaN() || !B.isNaN())) {
+ ResR = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * A;
+ ResI = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * B;
+ } else if ((A.isInfinity() || B.isInfinity()) && C.isFinite() &&
+ D.isFinite()) {
+ A = APFloat::copySign(
+ APFloat(A.getSemantics(), A.isInfinity() ? 1 : 0), A);
+ B = APFloat::copySign(
+ APFloat(B.getSemantics(), B.isInfinity() ? 1 : 0), B);
+ ResR = APFloat::getInf(ResR.getSemantics()) * (A * C + B * D);
+ ResI = APFloat::getInf(ResI.getSemantics()) * (B * C - A * D);
+ } else if (MaxCD.isInfinity() && A.isFinite() && B.isFinite()) {
+ C = APFloat::copySign(
+ APFloat(C.getSemantics(), C.isInfinity() ? 1 : 0), C);
+ D = APFloat::copySign(
+ APFloat(D.getSemantics(), D.isInfinity() ? 1 : 0), D);
+ ResR = APFloat::getZero(ResR.getSemantics()) * (A * C + B * D);
+ ResI = APFloat::getZero(ResI.getSemantics()) * (B * C - A * D);
+ }
+ }
+ }
} else {
if (RHS.getComplexIntReal() == 0 && RHS.getComplexIntImag() == 0)
return Error(E, diag::note_expr_divide_by_zero);
ComplexPairTy EmitBinMul(const BinOpInfo &Op);
ComplexPairTy EmitBinDiv(const BinOpInfo &Op);
+ ComplexPairTy EmitComplexBinOpLibCall(StringRef LibCallName,
+ const BinOpInfo &Op);
+
ComplexPairTy VisitBinAdd(const BinaryOperator *E) {
return EmitBinAdd(EmitBinOps(E));
}
if (Op.LHS.first->getType()->isFloatingPointTy()) {
ResR = Builder.CreateFAdd(Op.LHS.first, Op.RHS.first, "add.r");
- ResI = Builder.CreateFAdd(Op.LHS.second, Op.RHS.second, "add.i");
+ if (Op.LHS.second && Op.RHS.second)
+ ResI = Builder.CreateFAdd(Op.LHS.second, Op.RHS.second, "add.i");
+ else
+ ResI = Op.LHS.second ? Op.LHS.second : Op.RHS.second;
+ assert(ResI && "Only one operand may be real!");
} else {
ResR = Builder.CreateAdd(Op.LHS.first, Op.RHS.first, "add.r");
+ assert(Op.LHS.second && Op.RHS.second &&
+ "Both operands of integer complex operators must be complex!");
ResI = Builder.CreateAdd(Op.LHS.second, Op.RHS.second, "add.i");
}
return ComplexPairTy(ResR, ResI);
ComplexPairTy ComplexExprEmitter::EmitBinSub(const BinOpInfo &Op) {
llvm::Value *ResR, *ResI;
if (Op.LHS.first->getType()->isFloatingPointTy()) {
- ResR = Builder.CreateFSub(Op.LHS.first, Op.RHS.first, "sub.r");
- ResI = Builder.CreateFSub(Op.LHS.second, Op.RHS.second, "sub.i");
+ ResR = Builder.CreateFSub(Op.LHS.first, Op.RHS.first, "sub.r");
+ if (Op.LHS.second && Op.RHS.second)
+ ResI = Builder.CreateFSub(Op.LHS.second, Op.RHS.second, "sub.i");
+ else
+ ResI = Op.LHS.second ? Op.LHS.second
+ : Builder.CreateFNeg(Op.RHS.second, "sub.i");
+ assert(ResI && "Only one operand may be real!");
} else {
- ResR = Builder.CreateSub(Op.LHS.first, Op.RHS.first, "sub.r");
+ ResR = Builder.CreateSub(Op.LHS.first, Op.RHS.first, "sub.r");
+ assert(Op.LHS.second && Op.RHS.second &&
+ "Both operands of integer complex operators must be complex!");
ResI = Builder.CreateSub(Op.LHS.second, Op.RHS.second, "sub.i");
}
return ComplexPairTy(ResR, ResI);
}
+/// \brief Emit a libcall for a binary operation on complex types.
+ComplexPairTy ComplexExprEmitter::EmitComplexBinOpLibCall(StringRef LibCallName,
+ const BinOpInfo &Op) {
+ CallArgList Args;
+ Args.add(RValue::get(Op.LHS.first),
+ Op.Ty->castAs<ComplexType>()->getElementType());
+ Args.add(RValue::get(Op.LHS.second),
+ Op.Ty->castAs<ComplexType>()->getElementType());
+ Args.add(RValue::get(Op.RHS.first),
+ Op.Ty->castAs<ComplexType>()->getElementType());
+ Args.add(RValue::get(Op.RHS.second),
+ Op.Ty->castAs<ComplexType>()->getElementType());
+
+ // We *must* use the full CG function call building logic here because the
+ // complex type has special ABI handling.
+ const CGFunctionInfo &FuncInfo = CGF.CGM.getTypes().arrangeFreeFunctionCall(
+ Op.Ty, Args, FunctionType::ExtInfo(), RequiredArgs::All);
+ llvm::FunctionType *FTy = CGF.CGM.getTypes().GetFunctionType(FuncInfo);
+ llvm::Constant *Func = CGF.CGM.CreateRuntimeFunction(FTy, LibCallName);
+
+ llvm::Value *ArgVals[] = {Op.LHS.first, Op.LHS.second, Op.RHS.first,
+ Op.RHS.second};
+ llvm::Value *Result = CGF.EmitRuntimeCall(Func, ArgVals);
+ llvm::Value *ResR, *ResI;
+ if (Result->getType()->isVectorTy()) {
+ ResR = CGF.Builder.CreateExtractElement(Result, CGF.Builder.getInt32(0));
+ ResI = CGF.Builder.CreateExtractElement(Result, CGF.Builder.getInt32(1));
+ } else {
+ assert(Result->getType()->isAggregateType() &&
+ "Only vector and aggregate libcall returns are supported!");
+ unsigned ResRIndices[] = {0};
+ ResR = CGF.Builder.CreateExtractValue(Result, ResRIndices);
+ unsigned ResIIndices[] = {1};
+ ResI = CGF.Builder.CreateExtractValue(Result, ResIIndices);
+ }
+ return ComplexPairTy(ResR, ResI);
+}
+
+// See C11 Annex G.5.1 for the semantics of multiplicative operators on complex
+// typed values.
ComplexPairTy ComplexExprEmitter::EmitBinMul(const BinOpInfo &Op) {
using llvm::Value;
Value *ResR, *ResI;
if (Op.LHS.first->getType()->isFloatingPointTy()) {
- Value *ResRl = Builder.CreateFMul(Op.LHS.first, Op.RHS.first, "mul.rl");
- Value *ResRr = Builder.CreateFMul(Op.LHS.second, Op.RHS.second,"mul.rr");
- ResR = Builder.CreateFSub(ResRl, ResRr, "mul.r");
+ // The general formulation is:
+ // (a + ib) * (c + id) = (a * c - b * d) + i(a * d + b * c)
+ //
+ // But we can fold away components which would be zero due to a real
+ // operand according to C11 Annex G.5.1p2.
+ // FIXME: C11 also provides for imaginary types which would allow folding
+ // still more of this within the type system.
+
+ if (Op.LHS.second && Op.RHS.second) {
+ // If both operands are complex, delegate to a libcall which works to
+ // prevent underflow and overflow.
+ StringRef LibCallName;
+ switch (Op.LHS.first->getType()->getTypeID()) {
+ default:
+ llvm_unreachable("Unsupported floating point type!");
+ case llvm::Type::HalfTyID:
+ return EmitComplexBinOpLibCall("__mulhc3", Op);
+ case llvm::Type::FloatTyID:
+ return EmitComplexBinOpLibCall("__mulsc3", Op);
+ case llvm::Type::DoubleTyID:
+ return EmitComplexBinOpLibCall("__muldc3", Op);
+ case llvm::Type::X86_FP80TyID:
+ return EmitComplexBinOpLibCall("__mulxc3", Op);
+ }
+ }
+ assert((Op.LHS.second || Op.RHS.second) &&
+ "At least one operand must be complex!");
+
+ // If either of the operands is a real rather than a complex, the
+ // imaginary component is ignored when computing the real component of the
+ // result.
+ ResR = Builder.CreateFMul(Op.LHS.first, Op.RHS.first, "mul.rl");
- Value *ResIl = Builder.CreateFMul(Op.LHS.second, Op.RHS.first, "mul.il");
- Value *ResIr = Builder.CreateFMul(Op.LHS.first, Op.RHS.second, "mul.ir");
- ResI = Builder.CreateFAdd(ResIl, ResIr, "mul.i");
+ ResI = Op.LHS.second
+ ? Builder.CreateFMul(Op.LHS.second, Op.RHS.first, "mul.il")
+ : Builder.CreateFMul(Op.LHS.first, Op.RHS.second, "mul.ir");
} else {
+ assert(Op.LHS.second && Op.RHS.second &&
+ "Both operands of integer complex operators must be complex!");
Value *ResRl = Builder.CreateMul(Op.LHS.first, Op.RHS.first, "mul.rl");
- Value *ResRr = Builder.CreateMul(Op.LHS.second, Op.RHS.second,"mul.rr");
- ResR = Builder.CreateSub(ResRl, ResRr, "mul.r");
+ Value *ResRr = Builder.CreateMul(Op.LHS.second, Op.RHS.second, "mul.rr");
+ ResR = Builder.CreateSub(ResRl, ResRr, "mul.r");
Value *ResIl = Builder.CreateMul(Op.LHS.second, Op.RHS.first, "mul.il");
Value *ResIr = Builder.CreateMul(Op.LHS.first, Op.RHS.second, "mul.ir");
- ResI = Builder.CreateAdd(ResIl, ResIr, "mul.i");
+ ResI = Builder.CreateAdd(ResIl, ResIr, "mul.i");
}
return ComplexPairTy(ResR, ResI);
}
+// See C11 Annex G.5.1 for the semantics of multiplicative operators on complex
+// typed values.
ComplexPairTy ComplexExprEmitter::EmitBinDiv(const BinOpInfo &Op) {
llvm::Value *LHSr = Op.LHS.first, *LHSi = Op.LHS.second;
llvm::Value *RHSr = Op.RHS.first, *RHSi = Op.RHS.second;
llvm::Value *DSTr, *DSTi;
- if (Op.LHS.first->getType()->isFloatingPointTy()) {
- // (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
- llvm::Value *Tmp1 = Builder.CreateFMul(LHSr, RHSr); // a*c
- llvm::Value *Tmp2 = Builder.CreateFMul(LHSi, RHSi); // b*d
- llvm::Value *Tmp3 = Builder.CreateFAdd(Tmp1, Tmp2); // ac+bd
-
- llvm::Value *Tmp4 = Builder.CreateFMul(RHSr, RHSr); // c*c
- llvm::Value *Tmp5 = Builder.CreateFMul(RHSi, RHSi); // d*d
- llvm::Value *Tmp6 = Builder.CreateFAdd(Tmp4, Tmp5); // cc+dd
-
- llvm::Value *Tmp7 = Builder.CreateFMul(LHSi, RHSr); // b*c
- llvm::Value *Tmp8 = Builder.CreateFMul(LHSr, RHSi); // a*d
- llvm::Value *Tmp9 = Builder.CreateFSub(Tmp7, Tmp8); // bc-ad
+ if (LHSr->getType()->isFloatingPointTy()) {
+ // If we have a complex operand on the RHS, we delegate to a libcall to
+ // handle all of the complexities and minimize underflow/overflow cases.
+ //
+ // FIXME: We would be able to avoid the libcall in many places if we
+ // supported imaginary types in addition to complex types.
+ if (RHSi) {
+ BinOpInfo LibCallOp = Op;
+ // If LHS was a real, supply a null imaginary part.
+ if (!LHSi)
+ LibCallOp.LHS.second = llvm::Constant::getNullValue(LHSr->getType());
+
+ StringRef LibCallName;
+ switch (LHSr->getType()->getTypeID()) {
+ default:
+ llvm_unreachable("Unsupported floating point type!");
+ case llvm::Type::HalfTyID:
+ return EmitComplexBinOpLibCall("__divhc3", LibCallOp);
+ case llvm::Type::FloatTyID:
+ return EmitComplexBinOpLibCall("__divsc3", LibCallOp);
+ case llvm::Type::DoubleTyID:
+ return EmitComplexBinOpLibCall("__divdc3", LibCallOp);
+ case llvm::Type::X86_FP80TyID:
+ return EmitComplexBinOpLibCall("__divxc3", LibCallOp);
+ }
+ }
+ assert(LHSi && "Can have at most one non-complex operand!");
- DSTr = Builder.CreateFDiv(Tmp3, Tmp6);
- DSTi = Builder.CreateFDiv(Tmp9, Tmp6);
+ DSTr = Builder.CreateFDiv(LHSr, RHSr);
+ DSTi = Builder.CreateFDiv(LHSi, RHSr);
} else {
+ assert(Op.LHS.second && Op.RHS.second &&
+ "Both operands of integer complex operators must be complex!");
// (a+ib) / (c+id) = ((ac+bd)/(cc+dd)) + i((bc-ad)/(cc+dd))
llvm::Value *Tmp1 = Builder.CreateMul(LHSr, RHSr); // a*c
llvm::Value *Tmp2 = Builder.CreateMul(LHSi, RHSi); // b*d
TestAndClearIgnoreReal();
TestAndClearIgnoreImag();
BinOpInfo Ops;
- Ops.LHS = Visit(E->getLHS());
- Ops.RHS = Visit(E->getRHS());
+ if (E->getLHS()->getType()->isRealFloatingType())
+ Ops.LHS = ComplexPairTy(CGF.EmitScalarExpr(E->getLHS()), nullptr);
+ else
+ Ops.LHS = Visit(E->getLHS());
+ if (E->getRHS()->getType()->isRealFloatingType())
+ Ops.RHS = ComplexPairTy(CGF.EmitScalarExpr(E->getRHS()), nullptr);
+ else
+ Ops.RHS = Visit(E->getRHS());
+
Ops.Ty = E->getType();
return Ops;
}
// __block variables need to have the rhs evaluated first, plus this should
// improve codegen a little.
OpInfo.Ty = E->getComputationResultType();
+ QualType ComplexElementTy = cast<ComplexType>(OpInfo.Ty)->getElementType();
// The RHS should have been converted to the computation type.
- assert(OpInfo.Ty->isAnyComplexType());
- assert(CGF.getContext().hasSameUnqualifiedType(OpInfo.Ty,
- E->getRHS()->getType()));
- OpInfo.RHS = Visit(E->getRHS());
+ if (E->getRHS()->getType()->isRealFloatingType()) {
+ assert(
+ CGF.getContext()
+ .hasSameUnqualifiedType(ComplexElementTy, E->getRHS()->getType()));
+ OpInfo.RHS = ComplexPairTy(CGF.EmitScalarExpr(E->getRHS()), nullptr);
+ } else {
+ assert(CGF.getContext()
+ .hasSameUnqualifiedType(OpInfo.Ty, E->getRHS()->getType()));
+ OpInfo.RHS = Visit(E->getRHS());
+ }
LValue LHS = CGF.EmitLValue(E->getLHS());
OpInfo.LHS = EmitComplexToComplexCast(LHSVal, LHSTy, OpInfo.Ty);
} else {
llvm::Value *LHSVal = CGF.EmitLoadOfScalar(LHS, E->getExprLoc());
- OpInfo.LHS = EmitScalarToComplexCast(LHSVal, LHSTy, OpInfo.Ty);
+ // For floating point real operands we can directly pass the scalar form
+ // to the binary operator emission and potentially get more efficient code.
+ if (LHSTy->isRealFloatingType()) {
+ if (!CGF.getContext().hasSameUnqualifiedType(ComplexElementTy, LHSTy))
+ LHSVal = CGF.EmitScalarConversion(LHSVal, LHSTy, ComplexElementTy);
+ OpInfo.LHS = ComplexPairTy(LHSVal, nullptr);
+ } else {
+ OpInfo.LHS = EmitScalarToComplexCast(LHSVal, LHSTy, OpInfo.Ty);
+ }
}
// Expand the binary operator.
return false;
}
-/// \brief Takes two complex float types and converts them to the same type.
-/// Helper function of UsualArithmeticConversions()
-static QualType
-handleComplexFloatToComplexFloatConverstion(Sema &S, ExprResult &LHS,
- ExprResult &RHS, QualType LHSType,
- QualType RHSType,
- bool IsCompAssign) {
- int order = S.Context.getFloatingTypeOrder(LHSType, RHSType);
-
- if (order < 0) {
- // _Complex float -> _Complex double
- if (!IsCompAssign)
- LHS = S.ImpCastExprToType(LHS.get(), RHSType, CK_FloatingComplexCast);
- return RHSType;
- }
- if (order > 0)
- // _Complex float -> _Complex double
- RHS = S.ImpCastExprToType(RHS.get(), LHSType, CK_FloatingComplexCast);
- return LHSType;
-}
-
-/// \brief Converts otherExpr to complex float and promotes complexExpr if
-/// necessary. Helper function of UsualArithmeticConversions()
-static QualType handleOtherComplexFloatConversion(Sema &S,
- ExprResult &ComplexExpr,
- ExprResult &OtherExpr,
- QualType ComplexTy,
- QualType OtherTy,
- bool ConvertComplexExpr,
- bool ConvertOtherExpr) {
- int order = S.Context.getFloatingTypeOrder(ComplexTy, OtherTy);
-
- // If just the complexExpr is complex, the otherExpr needs to be converted,
- // and the complexExpr might need to be promoted.
- if (order > 0) { // complexExpr is wider
- // float -> _Complex double
- if (ConvertOtherExpr) {
- QualType fp = cast<ComplexType>(ComplexTy)->getElementType();
- OtherExpr = S.ImpCastExprToType(OtherExpr.get(), fp, CK_FloatingCast);
- OtherExpr = S.ImpCastExprToType(OtherExpr.get(), ComplexTy,
- CK_FloatingRealToComplex);
- }
- return ComplexTy;
- }
-
- // otherTy is at least as wide. Find its corresponding complex type.
- QualType result = (order == 0 ? ComplexTy :
- S.Context.getComplexType(OtherTy));
-
- // double -> _Complex double
- if (ConvertOtherExpr)
- OtherExpr = S.ImpCastExprToType(OtherExpr.get(), result,
- CK_FloatingRealToComplex);
-
- // _Complex float -> _Complex double
- if (ConvertComplexExpr && order < 0)
- ComplexExpr = S.ImpCastExprToType(ComplexExpr.get(), result,
- CK_FloatingComplexCast);
-
- return result;
-}
-
/// \brief Handle arithmetic conversion with complex types. Helper function of
/// UsualArithmeticConversions()
static QualType handleComplexFloatConversion(Sema &S, ExprResult &LHS,
// when combining a "long double" with a "double _Complex", the
// "double _Complex" is promoted to "long double _Complex".
- bool LHSComplexFloat = LHSType->isComplexType();
- bool RHSComplexFloat = RHSType->isComplexType();
-
- // If both are complex, just cast to the more precise type.
- if (LHSComplexFloat && RHSComplexFloat)
- return handleComplexFloatToComplexFloatConverstion(S, LHS, RHS,
- LHSType, RHSType,
- IsCompAssign);
-
- // If only one operand is complex, promote it if necessary and convert the
- // other operand to complex.
- if (LHSComplexFloat)
- return handleOtherComplexFloatConversion(
- S, LHS, RHS, LHSType, RHSType, /*convertComplexExpr*/!IsCompAssign,
- /*convertOtherExpr*/ true);
-
- assert(RHSComplexFloat);
- return handleOtherComplexFloatConversion(
- S, RHS, LHS, RHSType, LHSType, /*convertComplexExpr*/true,
- /*convertOtherExpr*/ !IsCompAssign);
+ // Compute the rank of the two types, regardless of whether they are complex.
+ int Order = S.Context.getFloatingTypeOrder(LHSType, RHSType);
+
+ auto *LHSComplexType = dyn_cast<ComplexType>(LHSType);
+ auto *RHSComplexType = dyn_cast<ComplexType>(RHSType);
+ QualType LHSElementType =
+ LHSComplexType ? LHSComplexType->getElementType() : LHSType;
+ QualType RHSElementType =
+ RHSComplexType ? RHSComplexType->getElementType() : RHSType;
+
+ QualType ResultType = S.Context.getComplexType(LHSElementType);
+ if (Order < 0) {
+ // Promote the precision of the LHS if not an assignment.
+ ResultType = S.Context.getComplexType(RHSElementType);
+ if (!IsCompAssign) {
+ if (LHSComplexType)
+ LHS =
+ S.ImpCastExprToType(LHS.get(), ResultType, CK_FloatingComplexCast);
+ else
+ LHS = S.ImpCastExprToType(LHS.get(), RHSElementType, CK_FloatingCast);
+ }
+ } else if (Order > 0) {
+ // Promote the precision of the RHS.
+ if (RHSComplexType)
+ RHS = S.ImpCastExprToType(RHS.get(), ResultType, CK_FloatingComplexCast);
+ else
+ RHS = S.ImpCastExprToType(RHS.get(), LHSElementType, CK_FloatingCast);
+ }
+ return ResultType;
}
/// \brief Hande arithmetic conversion from integer to float. Helper function
--- /dev/null
+// RUN: %clang_cc1 %s -O1 -emit-llvm -triple x86_64-unknown-unknown -o - | FileCheck %s --check-prefix=X86
+
+float _Complex add_float_rr(float a, float b) {
+ // X86-LABEL: @add_float_rr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+float _Complex add_float_cr(float _Complex a, float b) {
+ // X86-LABEL: @add_float_cr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+float _Complex add_float_rc(float a, float _Complex b) {
+ // X86-LABEL: @add_float_rc(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+float _Complex add_float_cc(float _Complex a, float _Complex b) {
+ // X86-LABEL: @add_float_cc(
+ // X86: fadd
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+
+float _Complex sub_float_rr(float a, float b) {
+ // X86-LABEL: @sub_float_rr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+float _Complex sub_float_cr(float _Complex a, float b) {
+ // X86-LABEL: @sub_float_cr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+float _Complex sub_float_rc(float a, float _Complex b) {
+ // X86-LABEL: @sub_float_rc(
+ // X86: fsub
+ // X86: fsub float -0.{{0+}}e+00,
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+float _Complex sub_float_cc(float _Complex a, float _Complex b) {
+ // X86-LABEL: @sub_float_cc(
+ // X86: fsub
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+
+float _Complex mul_float_rr(float a, float b) {
+ // X86-LABEL: @mul_float_rr(
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+float _Complex mul_float_cr(float _Complex a, float b) {
+ // X86-LABEL: @mul_float_cr(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+float _Complex mul_float_rc(float a, float _Complex b) {
+ // X86-LABEL: @mul_float_rc(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+float _Complex mul_float_cc(float _Complex a, float _Complex b) {
+ // X86-LABEL: @mul_float_cc(
+ // X86-NOT: fmul
+ // X86: call <2 x float> @__mulsc3(
+ // X86: ret
+ return a * b;
+}
+
+float _Complex div_float_rr(float a, float b) {
+ // X86-LABEL: @div_float_rr(
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+float _Complex div_float_cr(float _Complex a, float b) {
+ // X86-LABEL: @div_float_cr(
+ // X86: fdiv
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+float _Complex div_float_rc(float a, float _Complex b) {
+ // X86-LABEL: @div_float_rc(
+ // X86-NOT: fdiv
+ // X86: call <2 x float> @__divsc3(
+ // X86: ret
+ return a / b;
+}
+float _Complex div_float_cc(float _Complex a, float _Complex b) {
+ // X86-LABEL: @div_float_cc(
+ // X86-NOT: fdiv
+ // X86: call <2 x float> @__divsc3(
+ // X86: ret
+ return a / b;
+}
+
+double _Complex add_double_rr(double a, double b) {
+ // X86-LABEL: @add_double_rr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+double _Complex add_double_cr(double _Complex a, double b) {
+ // X86-LABEL: @add_double_cr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+double _Complex add_double_rc(double a, double _Complex b) {
+ // X86-LABEL: @add_double_rc(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+double _Complex add_double_cc(double _Complex a, double _Complex b) {
+ // X86-LABEL: @add_double_cc(
+ // X86: fadd
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+
+double _Complex sub_double_rr(double a, double b) {
+ // X86-LABEL: @sub_double_rr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+double _Complex sub_double_cr(double _Complex a, double b) {
+ // X86-LABEL: @sub_double_cr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+double _Complex sub_double_rc(double a, double _Complex b) {
+ // X86-LABEL: @sub_double_rc(
+ // X86: fsub
+ // X86: fsub double -0.{{0+}}e+00,
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+double _Complex sub_double_cc(double _Complex a, double _Complex b) {
+ // X86-LABEL: @sub_double_cc(
+ // X86: fsub
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+
+double _Complex mul_double_rr(double a, double b) {
+ // X86-LABEL: @mul_double_rr(
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+double _Complex mul_double_cr(double _Complex a, double b) {
+ // X86-LABEL: @mul_double_cr(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+double _Complex mul_double_rc(double a, double _Complex b) {
+ // X86-LABEL: @mul_double_rc(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+double _Complex mul_double_cc(double _Complex a, double _Complex b) {
+ // X86-LABEL: @mul_double_cc(
+ // X86-NOT: fmul
+ // X86: call { double, double } @__muldc3(
+ // X86: ret
+ return a * b;
+}
+
+double _Complex div_double_rr(double a, double b) {
+ // X86-LABEL: @div_double_rr(
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+double _Complex div_double_cr(double _Complex a, double b) {
+ // X86-LABEL: @div_double_cr(
+ // X86: fdiv
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+double _Complex div_double_rc(double a, double _Complex b) {
+ // X86-LABEL: @div_double_rc(
+ // X86-NOT: fdiv
+ // X86: call { double, double } @__divdc3(
+ // X86: ret
+ return a / b;
+}
+double _Complex div_double_cc(double _Complex a, double _Complex b) {
+ // X86-LABEL: @div_double_cc(
+ // X86-NOT: fdiv
+ // X86: call { double, double } @__divdc3(
+ // X86: ret
+ return a / b;
+}
+
+long double _Complex add_long_double_rr(long double a, long double b) {
+ // X86-LABEL: @add_long_double_rr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+long double _Complex add_long_double_cr(long double _Complex a, long double b) {
+ // X86-LABEL: @add_long_double_cr(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+long double _Complex add_long_double_rc(long double a, long double _Complex b) {
+ // X86-LABEL: @add_long_double_rc(
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+long double _Complex add_long_double_cc(long double _Complex a, long double _Complex b) {
+ // X86-LABEL: @add_long_double_cc(
+ // X86: fadd
+ // X86: fadd
+ // X86-NOT: fadd
+ // X86: ret
+ return a + b;
+}
+
+long double _Complex sub_long_double_rr(long double a, long double b) {
+ // X86-LABEL: @sub_long_double_rr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+long double _Complex sub_long_double_cr(long double _Complex a, long double b) {
+ // X86-LABEL: @sub_long_double_cr(
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+long double _Complex sub_long_double_rc(long double a, long double _Complex b) {
+ // X86-LABEL: @sub_long_double_rc(
+ // X86: fsub
+ // X86: fsub x86_fp80 0xK8{{0+}},
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+long double _Complex sub_long_double_cc(long double _Complex a, long double _Complex b) {
+ // X86-LABEL: @sub_long_double_cc(
+ // X86: fsub
+ // X86: fsub
+ // X86-NOT: fsub
+ // X86: ret
+ return a - b;
+}
+
+long double _Complex mul_long_double_rr(long double a, long double b) {
+ // X86-LABEL: @mul_long_double_rr(
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+long double _Complex mul_long_double_cr(long double _Complex a, long double b) {
+ // X86-LABEL: @mul_long_double_cr(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+long double _Complex mul_long_double_rc(long double a, long double _Complex b) {
+ // X86-LABEL: @mul_long_double_rc(
+ // X86: fmul
+ // X86: fmul
+ // X86-NOT: fmul
+ // X86: ret
+ return a * b;
+}
+long double _Complex mul_long_double_cc(long double _Complex a, long double _Complex b) {
+ // X86-LABEL: @mul_long_double_cc(
+ // X86-NOT: fmul
+ // X86: call { x86_fp80, x86_fp80 } @__mulxc3(
+ // X86: ret
+ return a * b;
+}
+
+long double _Complex div_long_double_rr(long double a, long double b) {
+ // X86-LABEL: @div_long_double_rr(
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+long double _Complex div_long_double_cr(long double _Complex a, long double b) {
+ // X86-LABEL: @div_long_double_cr(
+ // X86: fdiv
+ // X86: fdiv
+ // X86-NOT: fdiv
+ // X86: ret
+ return a / b;
+}
+long double _Complex div_long_double_rc(long double a, long double _Complex b) {
+ // X86-LABEL: @div_long_double_rc(
+ // X86-NOT: fdiv
+ // X86: call { x86_fp80, x86_fp80 } @__divxc3(
+ // X86: ret
+ return a / b;
+}
+long double _Complex div_long_double_cc(long double _Complex a, long double _Complex b) {
+ // X86-LABEL: @div_long_double_cc(
+ // X86-NOT: fdiv
+ // X86: call { x86_fp80, x86_fp80 } @__divxc3(
+ // X86: ret
+ return a / b;
+}
--- /dev/null
+// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify
+//
+// Test the constant folding of builtin complex numbers.
+
+static_assert((0.0 + 0.0j) == (0.0 + 0.0j));
+
+// Walk around the complex plane stepping between angular differences and
+// equality.
+static_assert((1.0 + 0.0j) == (0.0 + 0.0j)); // expected-error {{static_assert}}
+static_assert((1.0 + 0.0j) == (1.0 + 0.0j));
+static_assert((1.0 + 1.0j) == (1.0 + 0.0j)); // expected-error {{static_assert}}
+static_assert((1.0 + 1.0j) == (1.0 + 1.0j));
+static_assert((0.0 + 1.0j) == (1.0 + 1.0j)); // expected-error {{static_assert}}
+static_assert((0.0 + 1.0j) == (0.0 + 1.0j));
+static_assert((-1.0 + 1.0j) == (0.0 + 1.0j)); // expected-error {{static_assert}}
+static_assert((-1.0 + 1.0j) == (-1.0 + 1.0j));
+static_assert((-1.0 + 0.0j) == (-1.0 + 1.0j)); // expected-error {{static_assert}}
+static_assert((-1.0 + 0.0j) == (-1.0 + 0.0j));
+static_assert((-1.0 - 1.0j) == (-1.0 + 0.0j)); // expected-error {{static_assert}}
+static_assert((-1.0 - 1.0j) == (-1.0 - 1.0j));
+static_assert((0.0 - 1.0j) == (-1.0 - 1.0j)); // expected-error {{static_assert}}
+static_assert((0.0 - 1.0j) == (0.0 - 1.0j));
+static_assert((1.0 - 1.0j) == (0.0 - 1.0j)); // expected-error {{static_assert}}
+static_assert((1.0 - 1.0j) == (1.0 - 1.0j));
+
+// Test basic mathematical folding of both complex and real operands.
+static_assert(((1.0 + 0.5j) + (0.25 - 0.75j)) == (1.25 - 0.25j));
+static_assert(((1.0 + 0.5j) + 0.25) == (1.25 + 0.5j));
+static_assert((1.0 + (0.25 - 0.75j)) == (1.25 - 0.75j));
+
+static_assert(((1.0 + 0.5j) - (0.25 - 0.75j)) == (0.75 + 1.25j));
+static_assert(((1.0 + 0.5j) - 0.25) == (0.75 + 0.5j));
+static_assert((1.0 - (0.25 - 0.75j)) == (0.75 + 0.75j));
+
+static_assert(((1.25 + 0.5j) * (0.25 - 0.75j)) == (0.6875 - 0.8125j));
+static_assert(((1.25 + 0.5j) * 0.25) == (0.3125 + 0.125j));
+static_assert((1.25 * (0.25 - 0.75j)) == (0.3125 - 0.9375j));
+
+static_assert(((1.25 + 0.5j) / (0.25 - 0.75j)) == (-0.1 + 1.7j));
+static_assert(((1.25 + 0.5j) / 0.25) == (5.0 + 2.0j));
+static_assert((1.25 / (0.25 - 0.75j)) == (0.5 + 1.5j));
+
+// Test that infinities are preserved, don't turn into NaNs, and do form zeros
+// when the divisor.
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * 1.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * 1.0)) == 1);
+static_assert(__builtin_isinf_sign(__real__(1.0 * (__builtin_inf() + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__(1.0 * (1.0 + __builtin_inf() * 1.0j))) == 1);
+
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) * (__builtin_inf() + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) * (__builtin_inf() + 1.0j))) == 1);
+
+static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == -1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) * (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == 1);
+
+static_assert(__builtin_isinf_sign(__real__((1.0 + __builtin_inf() * 1.0j) * (1.0 + __builtin_inf() * 1.0j))) == -1);
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + __builtin_inf() * 1.0j) * (__builtin_inf() + __builtin_inf() * 1.0j))) == -1);
+
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (1.0 + 1.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / 1.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__(1.0 + (__builtin_inf() * 1.0j) / 1.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 1.0)) == 1);
+
+static_assert(((1.0 + 1.0j) / (__builtin_inf() + 1.0j)) == (0.0 + 0.0j));
+static_assert(((1.0 + 1.0j) / (1.0 + __builtin_inf() * 1.0j)) == (0.0 + 0.0j));
+static_assert(((1.0 + 1.0j) / (__builtin_inf() + __builtin_inf() * 1.0j)) == (0.0 + 0.0j));
+static_assert(((1.0 + 1.0j) / __builtin_inf()) == (0.0 + 0.0j));
+
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / (0.0 + 0.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / 0.0)) == 1);
+
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / (0.0 + 0.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1);
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / (0.0 + 0.0j))) == 1);
+static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / 0.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((1.0 + __builtin_inf() * 1.0j) / 0.0)) == 1);
+static_assert(__builtin_isinf_sign(__imag__((__builtin_inf() + __builtin_inf() * 1.0j) / 0.0)) == 1);