Timm =?utf-8?q?Bäder?= <tbae...@redhat.com> Message-ID: In-Reply-To: <llvm.org/llvm/llvm-project/pull/94...@github.com>
llvmbot wrote: <!--LLVM PR SUMMARY COMMENT--> @llvm/pr-subscribers-clang Author: Timm Baeder (tbaederr) <details> <summary>Changes</summary> Depends on the multiplication PR. --- Patch is 23.94 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/94892.diff 6 Files Affected: - (modified) clang/lib/AST/ExprConstShared.h (+10) - (modified) clang/lib/AST/ExprConstant.cpp (+100-82) - (modified) clang/lib/AST/Interp/ByteCodeExprGen.cpp (+73-10) - (modified) clang/lib/AST/Interp/Interp.h (+129) - (modified) clang/lib/AST/Interp/Opcodes.td (+8) - (modified) clang/test/AST/Interp/complex.cpp (+46) ``````````diff diff --git a/clang/lib/AST/ExprConstShared.h b/clang/lib/AST/ExprConstShared.h index a97eac85abc69..2a7088e4e371a 100644 --- a/clang/lib/AST/ExprConstShared.h +++ b/clang/lib/AST/ExprConstShared.h @@ -14,6 +14,9 @@ #ifndef LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H #define LLVM_CLANG_LIB_AST_EXPRCONSTSHARED_H +namespace llvm { +class APFloat; +} namespace clang { class QualType; class LangOptions; @@ -56,4 +59,11 @@ enum class GCCTypeClass { GCCTypeClass EvaluateBuiltinClassifyType(QualType T, const LangOptions &LangOpts); +void HandleComplexComplexMul(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C, + llvm::APFloat D, llvm::APFloat &ResR, + llvm::APFloat &ResI); +void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C, + llvm::APFloat D, llvm::APFloat &ResR, + llvm::APFloat &ResI); + #endif diff --git a/clang/lib/AST/ExprConstant.cpp b/clang/lib/AST/ExprConstant.cpp index 86fb396fabe2d..c4c8319b9f9f3 100644 --- a/clang/lib/AST/ExprConstant.cpp +++ b/clang/lib/AST/ExprConstant.cpp @@ -15126,6 +15126,104 @@ bool ComplexExprEvaluator::VisitCastExpr(const CastExpr *E) { llvm_unreachable("unknown cast resulting in complex value"); } +void HandleComplexComplexMul(APFloat A, APFloat B, APFloat C, APFloat D, + APFloat &ResR, APFloat &ResI) { + // This is an implementation of complex multiplication according to the + // constraints laid out in C11 Annex G. The implementation uses the + // following naming scheme: + // (a + ib) * (c + id) + + 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); + } + } +} + +void HandleComplexComplexDiv(APFloat A, APFloat B, APFloat C, APFloat D, + APFloat &ResR, APFloat &ResI) { + // This is an implementation of complex division according to the + // constraints laid out in C11 Annex G. The implementation uses the + // following naming scheme: + // (a + ib) / (c + id) + + int DenomLogB = 0; + APFloat MaxCD = maxnum(abs(C), abs(D)); + if (MaxCD.isFinite()) { + DenomLogB = ilogb(MaxCD); + C = scalbn(C, -DenomLogB, APFloat::rmNearestTiesToEven); + D = scalbn(D, -DenomLogB, APFloat::rmNearestTiesToEven); + } + APFloat Denom = C * C + D * D; + ResR = + scalbn((A * C + B * D) / Denom, -DenomLogB, APFloat::rmNearestTiesToEven); + ResI = + scalbn((B * C - A * D) / Denom, -DenomLogB, APFloat::rmNearestTiesToEven); + 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); + } + } +} + bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma) return ExprEvaluatorBaseTy::VisitBinaryOperator(E); @@ -15225,55 +15323,7 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { !handleFloatFloatBinOp(Info, E, ResI, BO_Mul, B)) return false; } 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); - } - } + HandleComplexComplexMul(A, B, C, D, ResR, ResI); } } else { ComplexValue LHS = Result; @@ -15311,39 +15361,7 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) { // 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, APFloat::rmNearestTiesToEven); - D = scalbn(D, -DenomLogB, APFloat::rmNearestTiesToEven); - } - APFloat Denom = C * C + D * D; - ResR = scalbn((A * C + B * D) / Denom, -DenomLogB, - APFloat::rmNearestTiesToEven); - ResI = scalbn((B * C - A * D) / Denom, -DenomLogB, - APFloat::rmNearestTiesToEven); - 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); - } - } + HandleComplexComplexDiv(A, B, C, D, ResR, ResI); } } else { if (RHS.getComplexIntReal() == 0 && RHS.getComplexIntImag() == 0) diff --git a/clang/lib/AST/Interp/ByteCodeExprGen.cpp b/clang/lib/AST/Interp/ByteCodeExprGen.cpp index ff2b51e3fb6fa..077fd14bdd913 100644 --- a/clang/lib/AST/Interp/ByteCodeExprGen.cpp +++ b/clang/lib/AST/Interp/ByteCodeExprGen.cpp @@ -854,6 +854,35 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { if (const auto *AT = RHSType->getAs<AtomicType>()) RHSType = AT->getValueType(); + // For ComplexComplex Mul, we have special ops to make their implementation + // easier. + BinaryOperatorKind Op = E->getOpcode(); + if (Op == BO_Mul && LHSType->isAnyComplexType() && + RHSType->isAnyComplexType()) { + assert(classifyPrim(LHSType->getAs<ComplexType>()->getElementType()) == + classifyPrim(RHSType->getAs<ComplexType>()->getElementType())); + PrimType ElemT = + classifyPrim(LHSType->getAs<ComplexType>()->getElementType()); + if (!this->visit(LHS)) + return false; + if (!this->visit(RHS)) + return false; + return this->emitMulc(ElemT, E); + } + + if (Op == BO_Div && LHSType->isAnyComplexType() && + RHSType->isAnyComplexType()) { + assert(classifyPrim(LHSType->getAs<ComplexType>()->getElementType()) == + classifyPrim(RHSType->getAs<ComplexType>()->getElementType())); + PrimType ElemT = + classifyPrim(LHSType->getAs<ComplexType>()->getElementType()); + if (!this->visit(LHS)) + return false; + if (!this->visit(RHS)) + return false; + return this->emitDivc(ElemT, E); + } + // Evaluate LHS and save value to LHSOffset. bool LHSIsComplex; unsigned LHSOffset; @@ -897,22 +926,22 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { // For both LHS and RHS, either load the value from the complex pointer, or // directly from the local variable. For index 1 (i.e. the imaginary part), // just load 0 and do the operation anyway. - auto loadComplexValue = [this](bool IsComplex, unsigned ElemIndex, - unsigned Offset, const Expr *E) -> bool { + auto loadComplexValue = [this](bool IsComplex, bool LoadZero, + unsigned ElemIndex, unsigned Offset, + const Expr *E) -> bool { if (IsComplex) { if (!this->emitGetLocal(PT_Ptr, Offset, E)) return false; return this->emitArrayElemPop(classifyComplexElementType(E->getType()), ElemIndex, E); } - if (ElemIndex == 0) + if (ElemIndex == 0 || !LoadZero) return this->emitGetLocal(classifyPrim(E->getType()), Offset, E); return this->visitZeroInitializer(classifyPrim(E->getType()), E->getType(), E); }; // Now we can get pointers to the LHS and RHS from the offsets above. - BinaryOperatorKind Op = E->getOpcode(); for (unsigned ElemIndex = 0; ElemIndex != 2; ++ElemIndex) { // Result pointer for the store later. if (!this->DiscardResult) { @@ -920,15 +949,14 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { return false; } - if (!loadComplexValue(LHSIsComplex, ElemIndex, LHSOffset, LHS)) - return false; - - if (!loadComplexValue(RHSIsComplex, ElemIndex, RHSOffset, RHS)) - return false; - // The actual operation. switch (Op) { case BO_Add: + if (!loadComplexValue(LHSIsComplex, true, ElemIndex, LHSOffset, LHS)) + return false; + + if (!loadComplexValue(RHSIsComplex, true, ElemIndex, RHSOffset, RHS)) + return false; if (ResultElemT == PT_Float) { if (!this->emitAddf(getRoundingMode(E), E)) return false; @@ -938,6 +966,11 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { } break; case BO_Sub: + if (!loadComplexValue(LHSIsComplex, true, ElemIndex, LHSOffset, LHS)) + return false; + + if (!loadComplexValue(RHSIsComplex, true, ElemIndex, RHSOffset, RHS)) + return false; if (ResultElemT == PT_Float) { if (!this->emitSubf(getRoundingMode(E), E)) return false; @@ -946,6 +979,36 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) { return false; } break; + case BO_Mul: + if (!loadComplexValue(LHSIsComplex, false, ElemIndex, LHSOffset, LHS)) + return false; + + if (!loadComplexValue(RHSIsComplex, false, ElemIndex, RHSOffset, RHS)) + return false; + + if (ResultElemT == PT_Float) { + if (!this->emitMulf(getRoundingMode(E), E)) + return false; + } else { + if (!this->emitMul(ResultElemT, E)) + return false; + } + break; + case BO_Div: + if (!loadComplexValue(LHSIsComplex, false, ElemIndex, LHSOffset, LHS)) + return false; + + if (!loadComplexValue(RHSIsComplex, false, ElemIndex, RHSOffset, RHS)) + return false; + + if (ResultElemT == PT_Float) { + if (!this->emitDivf(getRoundingMode(E), E)) + return false; + } else { + if (!this->emitDiv(ResultElemT, E)) + return false; + } + break; default: return false; diff --git a/clang/lib/AST/Interp/Interp.h b/clang/lib/AST/Interp/Interp.h index f63711da90c7e..94218be250c04 100644 --- a/clang/lib/AST/Interp/Interp.h +++ b/clang/lib/AST/Interp/Interp.h @@ -13,6 +13,7 @@ #ifndef LLVM_CLANG_AST_INTERP_INTERP_H #define LLVM_CLANG_AST_INTERP_INTERP_H +#include "../ExprConstShared.h" #include "Boolean.h" #include "Floating.h" #include "Function.h" @@ -368,6 +369,134 @@ inline bool Mulf(InterpState &S, CodePtr OpPC, llvm::RoundingMode RM) { S.Stk.push<Floating>(Result); return CheckFloatResult(S, OpPC, Result, Status); } + +template <PrimType Name, class T = typename PrimConv<Name>::T> +inline bool Mulc(InterpState &S, CodePtr OpPC) { + const Pointer &RHS = S.Stk.pop<Pointer>(); + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &Result = S.Stk.peek<Pointer>(); + + if constexpr (std::is_same_v<T, Floating>) { + APFloat A = LHS.atIndex(0).deref<Floating>().getAPFloat(); + APFloat B = LHS.atIndex(1).deref<Floating>().getAPFloat(); + APFloat C = RHS.atIndex(0).deref<Floating>().getAPFloat(); + APFloat D = RHS.atIndex(1).deref<Floating>().getAPFloat(); + + APFloat ResR(A.getSemantics()); + APFloat ResI(A.getSemantics()); + HandleComplexComplexMul(A, B, C, D, ResR, ResI); + + // Copy into the result. + Result.atIndex(0).deref<Floating>() = Floating(ResR); + Result.atIndex(0).initialize(); + Result.atIndex(1).deref<Floating>() = Floating(ResI); + Result.atIndex(1).initialize(); + Result.initialize(); + } else { + // Integer element type. + const T &LHSR = LHS.atIndex(0).deref<T>(); + const T &LHSI = LHS.atIndex(1).deref<T>(); + const T &RHSR = RHS.atIndex(0).deref<T>(); + const T &RHSI = RHS.atIndex(1).deref<T>(); + unsigned Bits = LHSR.bitWidth(); + + // real(Result) = (real(LHS) * real(RHS)) - (imag(LHS) * imag(RHS)) + T A; + if (T::mul(LHSR, RHSR, Bits, &A)) + return false; + T B; + if (T::mul(LHSI, RHSI, Bits, &B)) + return false; + if (T::sub(A, B, Bits, &Result.atIndex(0).deref<T>())) + return false; + Result.atIndex(0).initialize(); + + // imag(Result) = (real(LHS) * imag(RHS)) + (imag(LHS) * real(RHS)) + if (T::mul(LHSR, RHSI, Bits, &A)) + return false; + if (T::mul(LHSI, RHSR, Bits, &B)) + return false; + if (T::add(A, B, Bits, &Result.atIndex(1).deref<T>())) + return false; + Result.atIndex(1).initialize(); + Result.initialize(); + } + + return true; +} + +template <PrimType Name, class T = typename PrimConv<Name>::T> +inline bool Divc(InterpState &S, CodePtr OpPC) { + const Pointer &RHS = S.Stk.pop<Pointer>(); + const Pointer &LHS = S.Stk.pop<Pointer>(); + const Pointer &Result = S.Stk.peek<Pointer>(); + + if constexpr (std::is_same_v<T, Floating>) { + APFloat A = LHS.atIndex(0).deref<Floating>().getAPFloat(); + APFloat B = LHS.atIndex(1).deref<Floating>().getAPFloat(); + APFloat C = RHS.atIndex(0).deref<Floating>().getAPFloat(); + APFloat D = RHS.atIndex(1).deref<Floating>().getAPFloat(); + + APFloat ResR(A.getSemantics()); + APFloat ResI(A.getSemantics()); + HandleComplexComplexDiv(A, B, C, D, ResR, ResI); + + // Copy into the result. + Result.atIndex(0).deref<Floating>() = Floating(ResR); + Result.atIndex(0).initialize(); + Result.atIndex(1).deref<Floating>() = Floating(ResI); + Result.atIndex(1).initialize(); + Result.initialize(); + } else { + // Integer element type. + const T &LHSR = LHS.atIndex(0).deref<T>(); + const T &LHSI = LHS.atIndex(1).deref<T>(); + const T &RHSR = RHS.atIndex(0).deref<T>(); + const T &RHSI = RHS.atIndex(1).deref<T>(); + unsigned Bits = LHSR.bitWidth(); + const T Zero = T::from(0, Bits); + + if (Compare(RHSR, Zero) == ComparisonCategoryResult::Equal && + Compare(RHSI, Zero) == ComparisonCategoryResult::Equal) { + const SourceInfo &E = S.Current->getSource(OpPC); + S.FFDiag(E, diag::note_expr_divide_by_zero); + return false; + } + + // Den = real(RHS)² + imag(RHS)² + T A, B; + if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B)) + return false; + T Den; + if (T::add(A, B, Bits, &Den)) + return false; + + // real(Result) = ((real(LHS) * real(RHS)) + (imag(LHS) * imag(RHS))) / Den + T &ResultR = Result.atIndex(0).deref<T>(); + T &ResultI = Result.atIndex(1).deref<T>(); + + if (T::mul(LHSR, RHSR, Bits, &A) || T::mul(LHSI, RHSI, Bits, &B)) + return false; + if (T::add(A, B, Bits, &ResultR)) + return false; + if (T::div(ResultR, Den, Bits, &ResultR)) + return false; + Result.atIndex(0).initialize(); + + // imag(Result) = ((imag(LHS) * real(RHS)) - (real(LHS) * imag(RHS))) / Den + if (T::mul(LHSI, RHSR, Bits, &A) || T::mul(LHSR, RHSI, Bits, &B)) + return false; + if (T::sub(A, B, Bits, &ResultI)) + return false; + if (T::div(ResultI, Den, Bits, &ResultI)) + return false; + Result.atIndex(1).initialize()... [truncated] `````````` </details> https://github.com/llvm/llvm-project/pull/94892 _______________________________________________ cfe-commits mailing list cfe-commits@lists.llvm.org https://lists.llvm.org/cgi-bin/mailman/listinfo/cfe-commits