[clang] [clang][Interp] Implement complex division (PR #94892)
via cfe-commits
cfe-commits at lists.llvm.org
Sun Jun 9 00:27:20 PDT 2024
Timm =?utf-8?q?Bäder?= <tbaeder at redhat.com>
Message-ID:
In-Reply-To: <llvm.org/llvm/llvm-project/pull/94892 at 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
More information about the cfe-commits
mailing list