Skip to content

Commit 4d7d45e

Browse files
authored
[clang][Interp] Implement complex division (#94892)
Share the implementation with the current interpreter.
1 parent 69753aa commit 4d7d45e

File tree

7 files changed

+196
-42
lines changed

7 files changed

+196
-42
lines changed

clang/lib/AST/ExprConstShared.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,5 +62,8 @@ GCCTypeClass EvaluateBuiltinClassifyType(QualType T,
6262
void HandleComplexComplexMul(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
6363
llvm::APFloat D, llvm::APFloat &ResR,
6464
llvm::APFloat &ResI);
65+
void HandleComplexComplexDiv(llvm::APFloat A, llvm::APFloat B, llvm::APFloat C,
66+
llvm::APFloat D, llvm::APFloat &ResR,
67+
llvm::APFloat &ResI);
6568

6669
#endif

clang/lib/AST/ExprConstant.cpp

Lines changed: 43 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -15189,6 +15189,48 @@ void HandleComplexComplexMul(APFloat A, APFloat B, APFloat C, APFloat D,
1518915189
}
1519015190
}
1519115191

15192+
void HandleComplexComplexDiv(APFloat A, APFloat B, APFloat C, APFloat D,
15193+
APFloat &ResR, APFloat &ResI) {
15194+
// This is an implementation of complex division according to the
15195+
// constraints laid out in C11 Annex G. The implementation uses the
15196+
// following naming scheme:
15197+
// (a + ib) / (c + id)
15198+
15199+
int DenomLogB = 0;
15200+
APFloat MaxCD = maxnum(abs(C), abs(D));
15201+
if (MaxCD.isFinite()) {
15202+
DenomLogB = ilogb(MaxCD);
15203+
C = scalbn(C, -DenomLogB, APFloat::rmNearestTiesToEven);
15204+
D = scalbn(D, -DenomLogB, APFloat::rmNearestTiesToEven);
15205+
}
15206+
APFloat Denom = C * C + D * D;
15207+
ResR =
15208+
scalbn((A * C + B * D) / Denom, -DenomLogB, APFloat::rmNearestTiesToEven);
15209+
ResI =
15210+
scalbn((B * C - A * D) / Denom, -DenomLogB, APFloat::rmNearestTiesToEven);
15211+
if (ResR.isNaN() && ResI.isNaN()) {
15212+
if (Denom.isPosZero() && (!A.isNaN() || !B.isNaN())) {
15213+
ResR = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * A;
15214+
ResI = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * B;
15215+
} else if ((A.isInfinity() || B.isInfinity()) && C.isFinite() &&
15216+
D.isFinite()) {
15217+
A = APFloat::copySign(APFloat(A.getSemantics(), A.isInfinity() ? 1 : 0),
15218+
A);
15219+
B = APFloat::copySign(APFloat(B.getSemantics(), B.isInfinity() ? 1 : 0),
15220+
B);
15221+
ResR = APFloat::getInf(ResR.getSemantics()) * (A * C + B * D);
15222+
ResI = APFloat::getInf(ResI.getSemantics()) * (B * C - A * D);
15223+
} else if (MaxCD.isInfinity() && A.isFinite() && B.isFinite()) {
15224+
C = APFloat::copySign(APFloat(C.getSemantics(), C.isInfinity() ? 1 : 0),
15225+
C);
15226+
D = APFloat::copySign(APFloat(D.getSemantics(), D.isInfinity() ? 1 : 0),
15227+
D);
15228+
ResR = APFloat::getZero(ResR.getSemantics()) * (A * C + B * D);
15229+
ResI = APFloat::getZero(ResI.getSemantics()) * (B * C - A * D);
15230+
}
15231+
}
15232+
}
15233+
1519215234
bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
1519315235
if (E->isPtrMemOp() || E->isAssignmentOp() || E->getOpcode() == BO_Comma)
1519415236
return ExprEvaluatorBaseTy::VisitBinaryOperator(E);
@@ -15326,39 +15368,7 @@ bool ComplexExprEvaluator::VisitBinaryOperator(const BinaryOperator *E) {
1532615368
// No real optimizations we can do here, stub out with zero.
1532715369
B = APFloat::getZero(A.getSemantics());
1532815370
}
15329-
int DenomLogB = 0;
15330-
APFloat MaxCD = maxnum(abs(C), abs(D));
15331-
if (MaxCD.isFinite()) {
15332-
DenomLogB = ilogb(MaxCD);
15333-
C = scalbn(C, -DenomLogB, APFloat::rmNearestTiesToEven);
15334-
D = scalbn(D, -DenomLogB, APFloat::rmNearestTiesToEven);
15335-
}
15336-
APFloat Denom = C * C + D * D;
15337-
ResR = scalbn((A * C + B * D) / Denom, -DenomLogB,
15338-
APFloat::rmNearestTiesToEven);
15339-
ResI = scalbn((B * C - A * D) / Denom, -DenomLogB,
15340-
APFloat::rmNearestTiesToEven);
15341-
if (ResR.isNaN() && ResI.isNaN()) {
15342-
if (Denom.isPosZero() && (!A.isNaN() || !B.isNaN())) {
15343-
ResR = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * A;
15344-
ResI = APFloat::getInf(ResR.getSemantics(), C.isNegative()) * B;
15345-
} else if ((A.isInfinity() || B.isInfinity()) && C.isFinite() &&
15346-
D.isFinite()) {
15347-
A = APFloat::copySign(
15348-
APFloat(A.getSemantics(), A.isInfinity() ? 1 : 0), A);
15349-
B = APFloat::copySign(
15350-
APFloat(B.getSemantics(), B.isInfinity() ? 1 : 0), B);
15351-
ResR = APFloat::getInf(ResR.getSemantics()) * (A * C + B * D);
15352-
ResI = APFloat::getInf(ResI.getSemantics()) * (B * C - A * D);
15353-
} else if (MaxCD.isInfinity() && A.isFinite() && B.isFinite()) {
15354-
C = APFloat::copySign(
15355-
APFloat(C.getSemantics(), C.isInfinity() ? 1 : 0), C);
15356-
D = APFloat::copySign(
15357-
APFloat(D.getSemantics(), D.isInfinity() ? 1 : 0), D);
15358-
ResR = APFloat::getZero(ResR.getSemantics()) * (A * C + B * D);
15359-
ResI = APFloat::getZero(ResI.getSemantics()) * (B * C - A * D);
15360-
}
15361-
}
15371+
HandleComplexComplexDiv(A, B, C, D, ResR, ResI);
1536215372
}
1536315373
} else {
1536415374
if (RHS.getComplexIntReal() == 0 && RHS.getComplexIntImag() == 0)

clang/lib/AST/Interp/ByteCodeExprGen.cpp

Lines changed: 58 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -891,11 +891,14 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
891891
if (const auto *AT = RHSType->getAs<AtomicType>())
892892
RHSType = AT->getValueType();
893893

894+
bool LHSIsComplex = LHSType->isAnyComplexType();
895+
unsigned LHSOffset;
896+
bool RHSIsComplex = RHSType->isAnyComplexType();
897+
894898
// For ComplexComplex Mul, we have special ops to make their implementation
895899
// easier.
896900
BinaryOperatorKind Op = E->getOpcode();
897-
if (Op == BO_Mul && LHSType->isAnyComplexType() &&
898-
RHSType->isAnyComplexType()) {
901+
if (Op == BO_Mul && LHSIsComplex && RHSIsComplex) {
899902
assert(classifyPrim(LHSType->getAs<ComplexType>()->getElementType()) ==
900903
classifyPrim(RHSType->getAs<ComplexType>()->getElementType()));
901904
PrimType ElemT =
@@ -907,18 +910,51 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
907910
return this->emitMulc(ElemT, E);
908911
}
909912

913+
if (Op == BO_Div && RHSIsComplex) {
914+
QualType ElemQT = RHSType->getAs<ComplexType>()->getElementType();
915+
PrimType ElemT = classifyPrim(ElemQT);
916+
// If the LHS is not complex, we still need to do the full complex
917+
// division, so just stub create a complex value and stub it out with
918+
// the LHS and a zero.
919+
920+
if (!LHSIsComplex) {
921+
// This is using the RHS type for the fake-complex LHS.
922+
if (auto LHSO = allocateLocal(RHS))
923+
LHSOffset = *LHSO;
924+
else
925+
return false;
926+
927+
if (!this->emitGetPtrLocal(LHSOffset, E))
928+
return false;
929+
930+
if (!this->visit(LHS))
931+
return false;
932+
// real is LHS
933+
if (!this->emitInitElem(ElemT, 0, E))
934+
return false;
935+
// imag is zero
936+
if (!this->visitZeroInitializer(ElemT, ElemQT, E))
937+
return false;
938+
if (!this->emitInitElem(ElemT, 1, E))
939+
return false;
940+
} else {
941+
if (!this->visit(LHS))
942+
return false;
943+
}
944+
945+
if (!this->visit(RHS))
946+
return false;
947+
return this->emitDivc(ElemT, E);
948+
}
949+
910950
// Evaluate LHS and save value to LHSOffset.
911-
bool LHSIsComplex;
912-
unsigned LHSOffset;
913951
if (LHSType->isAnyComplexType()) {
914-
LHSIsComplex = true;
915952
LHSOffset = this->allocateLocalPrimitive(LHS, PT_Ptr, true, false);
916953
if (!this->visit(LHS))
917954
return false;
918955
if (!this->emitSetLocal(PT_Ptr, LHSOffset, E))
919956
return false;
920957
} else {
921-
LHSIsComplex = false;
922958
PrimType LHST = classifyPrim(LHSType);
923959
LHSOffset = this->allocateLocalPrimitive(LHS, LHST, true, false);
924960
if (!this->visit(LHS))
@@ -928,17 +964,14 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
928964
}
929965

930966
// Same with RHS.
931-
bool RHSIsComplex;
932967
unsigned RHSOffset;
933968
if (RHSType->isAnyComplexType()) {
934-
RHSIsComplex = true;
935969
RHSOffset = this->allocateLocalPrimitive(RHS, PT_Ptr, true, false);
936970
if (!this->visit(RHS))
937971
return false;
938972
if (!this->emitSetLocal(PT_Ptr, RHSOffset, E))
939973
return false;
940974
} else {
941-
RHSIsComplex = false;
942975
PrimType RHST = classifyPrim(RHSType);
943976
RHSOffset = this->allocateLocalPrimitive(RHS, RHST, true, false);
944977
if (!this->visit(RHS))
@@ -1018,6 +1051,22 @@ bool ByteCodeExprGen<Emitter>::VisitComplexBinOp(const BinaryOperator *E) {
10181051
return false;
10191052
}
10201053
break;
1054+
case BO_Div:
1055+
assert(!RHSIsComplex);
1056+
if (!loadComplexValue(LHSIsComplex, false, ElemIndex, LHSOffset, LHS))
1057+
return false;
1058+
1059+
if (!loadComplexValue(RHSIsComplex, false, ElemIndex, RHSOffset, RHS))
1060+
return false;
1061+
1062+
if (ResultElemT == PT_Float) {
1063+
if (!this->emitDivf(getRoundingMode(E), E))
1064+
return false;
1065+
} else {
1066+
if (!this->emitDiv(ResultElemT, E))
1067+
return false;
1068+
}
1069+
break;
10211070

10221071
default:
10231072
return false;

clang/lib/AST/Interp/Interp.h

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -425,6 +425,78 @@ inline bool Mulc(InterpState &S, CodePtr OpPC) {
425425
return true;
426426
}
427427

428+
template <PrimType Name, class T = typename PrimConv<Name>::T>
429+
inline bool Divc(InterpState &S, CodePtr OpPC) {
430+
const Pointer &RHS = S.Stk.pop<Pointer>();
431+
const Pointer &LHS = S.Stk.pop<Pointer>();
432+
const Pointer &Result = S.Stk.peek<Pointer>();
433+
434+
if constexpr (std::is_same_v<T, Floating>) {
435+
APFloat A = LHS.atIndex(0).deref<Floating>().getAPFloat();
436+
APFloat B = LHS.atIndex(1).deref<Floating>().getAPFloat();
437+
APFloat C = RHS.atIndex(0).deref<Floating>().getAPFloat();
438+
APFloat D = RHS.atIndex(1).deref<Floating>().getAPFloat();
439+
440+
APFloat ResR(A.getSemantics());
441+
APFloat ResI(A.getSemantics());
442+
HandleComplexComplexDiv(A, B, C, D, ResR, ResI);
443+
444+
// Copy into the result.
445+
Result.atIndex(0).deref<Floating>() = Floating(ResR);
446+
Result.atIndex(0).initialize();
447+
Result.atIndex(1).deref<Floating>() = Floating(ResI);
448+
Result.atIndex(1).initialize();
449+
Result.initialize();
450+
} else {
451+
// Integer element type.
452+
const T &LHSR = LHS.atIndex(0).deref<T>();
453+
const T &LHSI = LHS.atIndex(1).deref<T>();
454+
const T &RHSR = RHS.atIndex(0).deref<T>();
455+
const T &RHSI = RHS.atIndex(1).deref<T>();
456+
unsigned Bits = LHSR.bitWidth();
457+
const T Zero = T::from(0, Bits);
458+
459+
if (Compare(RHSR, Zero) == ComparisonCategoryResult::Equal &&
460+
Compare(RHSI, Zero) == ComparisonCategoryResult::Equal) {
461+
const SourceInfo &E = S.Current->getSource(OpPC);
462+
S.FFDiag(E, diag::note_expr_divide_by_zero);
463+
return false;
464+
}
465+
466+
// Den = real(RHS)² + imag(RHS)²
467+
T A, B;
468+
if (T::mul(RHSR, RHSR, Bits, &A) || T::mul(RHSI, RHSI, Bits, &B))
469+
return false;
470+
T Den;
471+
if (T::add(A, B, Bits, &Den))
472+
return false;
473+
474+
// real(Result) = ((real(LHS) * real(RHS)) + (imag(LHS) * imag(RHS))) / Den
475+
T &ResultR = Result.atIndex(0).deref<T>();
476+
T &ResultI = Result.atIndex(1).deref<T>();
477+
478+
if (T::mul(LHSR, RHSR, Bits, &A) || T::mul(LHSI, RHSI, Bits, &B))
479+
return false;
480+
if (T::add(A, B, Bits, &ResultR))
481+
return false;
482+
if (T::div(ResultR, Den, Bits, &ResultR))
483+
return false;
484+
Result.atIndex(0).initialize();
485+
486+
// imag(Result) = ((imag(LHS) * real(RHS)) - (real(LHS) * imag(RHS))) / Den
487+
if (T::mul(LHSI, RHSR, Bits, &A) || T::mul(LHSR, RHSI, Bits, &B))
488+
return false;
489+
if (T::sub(A, B, Bits, &ResultI))
490+
return false;
491+
if (T::div(ResultI, Den, Bits, &ResultI))
492+
return false;
493+
Result.atIndex(1).initialize();
494+
Result.initialize();
495+
}
496+
497+
return true;
498+
}
499+
428500
/// 1) Pops the RHS from the stack.
429501
/// 2) Pops the LHS from the stack.
430502
/// 3) Pushes 'LHS & RHS' on the stack

clang/lib/AST/Interp/Opcodes.td

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -533,6 +533,10 @@ def Mulc : Opcode {
533533
def Rem : IntegerOpcode;
534534
def Div : IntegerOpcode;
535535
def Divf : FloatOpcode;
536+
def Divc : Opcode {
537+
let Types = [NumberTypeClass];
538+
let HasGroup = 1;
539+
}
536540

537541
def BitAnd : IntegerOpcode;
538542
def BitOr : IntegerOpcode;

clang/test/AST/Interp/complex.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,21 @@ constexpr _Complex int IIMC = IIMA * IIMB;
4040
static_assert(__real(IIMC) == -30, "");
4141
static_assert(__imag(IIMC) == 40, "");
4242

43+
static_assert(1.0j / 0.0 == 1); // both-error {{static assertion}} \
44+
// both-note {{division by zero}}
45+
static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / (0.0 + 0.0j))) == 1);
46+
static_assert(__builtin_isinf_sign(__real__((1.0 + 1.0j) / 0.0)) == 1); // both-error {{static assertion}} \
47+
// both-note {{division by zero}}
48+
static_assert(__builtin_isinf_sign(__real__((__builtin_inf() + 1.0j) / (0.0 + 0.0j))) == 1);
49+
static_assert(__builtin_isinf_sign(__imag__((1.0 + InfC) / (0.0 + 0.0j))) == 1);
50+
static_assert(__builtin_isinf_sign(__imag__((InfInf) / (0.0 + 0.0j))) == 1);
51+
52+
constexpr _Complex int IIDA = {10,20};
53+
constexpr _Complex int IIDB = {1,2};
54+
constexpr _Complex int IIDC = IIDA / IIDB;
55+
static_assert(__real(IIDC) == 10, "");
56+
static_assert(__imag(IIDC) == 0, "");
57+
4358
constexpr _Complex int Comma1 = {1, 2};
4459
constexpr _Complex int Comma2 = (0, Comma1);
4560
static_assert(Comma1 == Comma1, "");

clang/test/SemaCXX/complex-folding.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify
2+
// RUN: %clang_cc1 %s -std=c++1z -fsyntax-only -verify -fexperimental-new-constant-interpreter
23
//
34
// Test the constant folding of builtin complex numbers.
45

0 commit comments

Comments
 (0)