Index: lib/AST/ExprConstant.cpp =================================================================== --- lib/AST/ExprConstant.cpp +++ lib/AST/ExprConstant.cpp @@ -7690,7 +7690,11 @@ static bool EvaluateComplex(const Expr *E, ComplexValue &Result, EvalInfo &Info) { - assert(E->isRValue() && E->getType()->isAnyComplexType()); + assert(E->isRValue() && "Can only evaluate R-values!"); + assert((E->getType()->isAnyComplexType() || + E->getType()->isRealFloatingType()) && + "Complex expressions can only be composed of complex and real " + "floating point types."); return ComplexExprEvaluator(Info, Result).Visit(E); } @@ -7874,12 +7878,32 @@ 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()) { + 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()) { + 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() && @@ -7890,8 +7914,11 @@ 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(); @@ -7901,8 +7928,13 @@ 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(); @@ -7919,16 +7951,26 @@ 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); + if (!LHSReal) { + 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); + if (!RHSReal) { + Tmp = LHS_r; + Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven); + Result.getComplexFloatImag() = Tmp; + } + if (!LHSReal) { + Tmp = LHS_i; + Tmp.multiply(RHS_r, APFloat::rmNearestTiesToEven); + if (!RHSReal) + Result.getComplexFloatImag().add(Tmp, APFloat::rmNearestTiesToEven); + else + Result.getComplexFloatImag() = Tmp; + } } else { ComplexValue LHS = Result; Result.getComplexIntReal() = @@ -7949,24 +7991,38 @@ APFloat &Res_r = Result.getComplexFloatReal(); APFloat &Res_i = Result.getComplexFloatImag(); + APFloat Tmp(RHS_r.getSemantics()); 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); + if (!RHSReal) { + 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); + if (!LHSReal && !RHSReal) { + 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); + if (!RHSReal) { Tmp = LHS_r; Tmp.multiply(RHS_i, APFloat::rmNearestTiesToEven); - Res_i.subtract(Tmp, APFloat::rmNearestTiesToEven); + } + if (!LHSReal) { + Res_i = LHS_i; + Res_i.multiply(RHS_r, APFloat::rmNearestTiesToEven); + if (!RHSReal) + Res_i.subtract(Tmp, APFloat::rmNearestTiesToEven); + } else { + assert(!RHSReal && "Cannot have both operands be real!"); + Tmp.changeSign(); + Res_i = Tmp; + } Res_i.divide(Den, APFloat::rmNearestTiesToEven); } else { if (RHS.getComplexIntReal() == 0 && RHS.getComplexIntImag() == 0) Index: lib/CodeGen/CGExprComplex.cpp =================================================================== --- lib/CodeGen/CGExprComplex.cpp +++ lib/CodeGen/CGExprComplex.cpp @@ -230,6 +230,10 @@ 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)); } @@ -528,9 +532,15 @@ 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); @@ -539,63 +549,154 @@ 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()->getElementType()); + Args.add(RValue::get(Op.LHS.second), Op.Ty->castAs()->getElementType()); + Args.add(RValue::get(Op.RHS.first), Op.Ty->castAs()->getElementType()); + Args.add(RValue::get(Op.RHS.second), Op.Ty->castAs()->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!"); - 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"); + // 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"); + + 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 @@ -626,8 +727,15 @@ 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; } @@ -647,12 +755,18 @@ // __block variables need to have the rhs evaluated first, plus this should // improve codegen a little. OpInfo.Ty = E->getComputationResultType(); + QualType ComplexElementTy = cast(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()); @@ -662,7 +776,15 @@ 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. Index: lib/Sema/SemaExpr.cpp =================================================================== --- lib/Sema/SemaExpr.cpp +++ lib/Sema/SemaExpr.cpp @@ -938,68 +938,6 @@ 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(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, @@ -1025,26 +963,35 @@ // 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(LHSType); + auto *RHSComplexType = dyn_cast(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 Index: test/CodeGen/complex-math.c =================================================================== --- /dev/null +++ test/CodeGen/complex-math.c @@ -0,0 +1,367 @@ +// 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; +}