From c7b30daae8489be263d0f9960db4e6a7235b4348 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Sat, 29 Nov 2025 02:27:36 +0400 Subject: [PATCH 1/3] perf: fast divide shorter by longer bigint --- lib/Support/BigIntSupport.cpp | 28 +++++++++++++++++++++------- 1 file changed, 21 insertions(+), 7 deletions(-) diff --git a/lib/Support/BigIntSupport.cpp b/lib/Support/BigIntSupport.cpp index d9b0fe35b64..8afcad59498 100644 --- a/lib/Support/BigIntSupport.cpp +++ b/lib/Support/BigIntSupport.cpp @@ -1754,6 +1754,25 @@ static OperationStatus compute( ((quoc.digits != nullptr) != (rem.digits != nullptr)) && "untested -- calling with both or neither quoc and rem"); + // Signal division by zero. + if (compare(rhs, 0) == 0) { + return OperationStatus::DIVISION_BY_ZERO; + } + + if (lhs.numDigits < rhs.numDigits) { + // In this case, divideResultSize returns 0 and mismatches remainderResultSize + + if (quoc.digits != nullptr) { + quoc.numDigits = 0; + } + + if (rem.digits != nullptr) { + return initWithDigits(rem, lhs); + } + + return OperationStatus::RETURNED; + } + const uint32_t resultSize = divideResultSize(lhs, rhs); // set quoc's and rem's numDigits if their digits buffer is nullptr, which // allows querying either for determining the result size. @@ -1772,11 +1791,6 @@ static OperationStatus compute( quoc.numDigits = resultSize; rem.numDigits = resultSize; - // Signal division by zero. - if (compare(rhs, 0) == 0) { - return OperationStatus::DIVISION_BY_ZERO; - } - // tcDivide operates on unsigned number, so just like multiply, the operands // must be negated (and the result as well, if appropriate) if they are // negative. @@ -1868,7 +1882,7 @@ static OperationStatus compute( } // namespace uint32_t divideResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - return div_rem::getResultSize(lhs, rhs); + return lhs.numDigits < rhs.numDigits ? 0 : div_rem::getResultSize(lhs, rhs); } OperationStatus @@ -1879,7 +1893,7 @@ divide(MutableBigIntRef dst, ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { } uint32_t remainderResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - return div_rem::getResultSize(lhs, rhs); + return lhs.numDigits < rhs.numDigits ? lhs.numDigits : div_rem::getResultSize(lhs, rhs); } OperationStatus remainder( From 3ba8ab7b3e1c1584cce0aee2d0210a7a34f55134 Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Sat, 29 Nov 2025 03:43:42 +0400 Subject: [PATCH 2/3] perf: do not allocate extra digit in bigint division --- lib/Support/BigIntSupport.cpp | 25 +++++++++++++++++++------ 1 file changed, 19 insertions(+), 6 deletions(-) diff --git a/lib/Support/BigIntSupport.cpp b/lib/Support/BigIntSupport.cpp index 8afcad59498..fd56f77b885 100644 --- a/lib/Support/BigIntSupport.cpp +++ b/lib/Support/BigIntSupport.cpp @@ -1741,10 +1741,6 @@ multiply(MutableBigIntRef dst, ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { namespace { namespace div_rem { -static uint32_t getResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - return std::max(lhs.numDigits, rhs.numDigits) + 1; -} - static OperationStatus compute( MutableBigIntRef quoc, MutableBigIntRef rem, @@ -1882,7 +1878,19 @@ static OperationStatus compute( } // namespace uint32_t divideResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - return lhs.numDigits < rhs.numDigits ? 0 : div_rem::getResultSize(lhs, rhs); + if (lhs.numDigits < rhs.numDigits) { + // Special case: (res = 0, rem = lhs), regardless of rhs sign + return 0; + } + + if (compare(rhs, -1) == 0 && isNegative(lhs)) { + // In this (and only this) case, we can end up with more digits than we started with + // Examples: -(2n**63n)/-1n, -(2n**127n)/-1n + // We avoid this in general case as it makes division much slower + return lhs.numDigits + 1; + } + + return lhs.numDigits; } OperationStatus @@ -1893,7 +1901,12 @@ divide(MutableBigIntRef dst, ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { } uint32_t remainderResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - return lhs.numDigits < rhs.numDigits ? lhs.numDigits : div_rem::getResultSize(lhs, rhs); + if (lhs.numDigits < rhs.numDigits) { + // Special case: (res = 0, rem = lhs), regardless of rhs sign + return lhs.numDigits; + } + + return divideResultSize(lhs, rhs); // We currently expect them to be equivalent in div_rem::compute() } OperationStatus remainder( From 5e020657c76cb70d2955f729349e0b68a839143a Mon Sep 17 00:00:00 2001 From: Nikita Skovoroda Date: Sun, 7 Dec 2025 06:16:35 +0400 Subject: [PATCH 3/3] fix --- lib/Support/BigIntSupport.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/lib/Support/BigIntSupport.cpp b/lib/Support/BigIntSupport.cpp index fd56f77b885..4383706857b 100644 --- a/lib/Support/BigIntSupport.cpp +++ b/lib/Support/BigIntSupport.cpp @@ -1755,7 +1755,7 @@ static OperationStatus compute( return OperationStatus::DIVISION_BY_ZERO; } - if (lhs.numDigits < rhs.numDigits) { + if (lhs.numDigits + 1 < rhs.numDigits) { // In this case, divideResultSize returns 0 and mismatches remainderResultSize if (quoc.digits != nullptr) { @@ -1878,8 +1878,9 @@ static OperationStatus compute( } // namespace uint32_t divideResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - if (lhs.numDigits < rhs.numDigits) { + if (lhs.numDigits + 1 < rhs.numDigits) { // Special case: (res = 0, rem = lhs), regardless of rhs sign + // Can't use lhs.numDigits < rhs.numDigits here because -(2n**63n)/(2n**63n) is not 0 return 0; } @@ -1901,8 +1902,9 @@ divide(MutableBigIntRef dst, ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { } uint32_t remainderResultSize(ImmutableBigIntRef lhs, ImmutableBigIntRef rhs) { - if (lhs.numDigits < rhs.numDigits) { + if (lhs.numDigits + 1 < rhs.numDigits) { // Special case: (res = 0, rem = lhs), regardless of rhs sign + // Can't use lhs.numDigits < rhs.numDigits here because -(2n**63n)/(2n**63n) is not 0 return lhs.numDigits; }