Skip to content

Commit 7a0c4b5

Browse files
Add default implementations for FixedWidthInteger.dividingFullWidth (#70823)
These are provided for FixedWidthInteger & UnsignedInteger (the base implementation, following Knuth's Algorithm D) and SignedInteger (converting to magnitudes and calling the former). Previously no default implementations were available, requiring every type to implement these operations. These defaults will not be optimal for large fixed-width integers, so types vending Int512 or similar integers should still provide their own implementations, but they are unconditionally available as a fallback, which simplifies the process of writing such types, and work well enough as a fallback for modest fixed-width integer types like Int64 or 32b or smaller platforms or Int128 on 64b platforms. Additionally rework the concrete implementations to guarantee that we always trap when the quotient is not representable, and to improve performance for 64b integers on arm64_32, and added some new test coverage for these operations.
1 parent 92b20eb commit 7a0c4b5

File tree

4 files changed

+462
-22
lines changed

4 files changed

+462
-22
lines changed

stdlib/public/core/IntegerTypes.swift.gyb

Lines changed: 50 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1569,12 +1569,17 @@ ${assignmentOperatorComment(x.operator, True)}
15691569
#endif
15701570
% end
15711571

1572+
% if bits == 64:
1573+
#if _pointerBitWidth(_64) || arch(arm64_32)
1574+
// On 32b architectures we fall back on the generic implementation,
1575+
// because LLVM doesn't know how to codegen the 128b divide we use.
1576+
% end
15721577
/// Returns a tuple containing the quotient and remainder of dividing the
15731578
/// given value by this value.
15741579
///
15751580
/// The resulting quotient must be representable within the bounds of the
15761581
/// type. If the quotient of dividing `dividend` by this value is too large
1577-
/// to represent in the type, a runtime error may occur.
1582+
/// to represent in the type, a runtime error will occur.
15781583
///
15791584
/// - Parameter dividend: A tuple containing the high and low parts of a
15801585
/// double-width integer. The `high` component of the value carries the
@@ -1585,34 +1590,58 @@ ${assignmentOperatorComment(x.operator, True)}
15851590
public func dividingFullWidth(
15861591
_ dividend: (high: ${Self}, low: ${Self}.Magnitude)
15871592
) -> (quotient: ${Self}, remainder: ${Self}) {
1588-
// FIXME(integers): tests
1589-
% # 128-bit types are not provided by the 32-bit LLVM
1590-
% if word_bits == 32 and bits == 64:
1591-
% # FIXME(integers): uncomment the above after using the right conditional
1592-
% # compilation block to exclude 64-bit Windows, which does not support
1593-
% # 128-bit operations
1594-
fatalError("Operation is not supported")
1595-
% else:
1596-
// FIXME(integers): handle division by zero and overflows
15971593
_precondition(self != 0, "Division by zero")
1594+
% if not signed:
1595+
_precondition(self > dividend.high, "Quotient is not representable")
1596+
% end
1597+
% if dbits <= 64:
1598+
% DoubleType = ('Int' if signed else 'UInt') + str(dbits)
1599+
let a = ${DoubleType}(dividend.high) &<< ${bits} |
1600+
${DoubleType}(dividend.low)
1601+
let b = ${DoubleType}(self)
1602+
let (q, r) = a.quotientAndRemainder(dividingBy: b)
1603+
% if signed:
1604+
// remainder is guaranteed representable, but the quotient might not be
1605+
// so we need to use a conversion that will check for us:
1606+
guard let quotient = ${Self}(exactly: q) else {
1607+
_preconditionFailure("Quotient is not representable")
1608+
}
1609+
return (quotient, ${Self}(truncatingIfNeeded: r))
1610+
% else:
1611+
// quotient and remainder are both guaranteed to be representable by
1612+
// the precondition we enforced above and the definition of division.
1613+
return (${Self}(truncatingIfNeeded: q), ${Self}(truncatingIfNeeded: r))
1614+
% end
1615+
% else:
15981616
let lhsHigh = Builtin.${z}ext_Int${bits}_Int${dbits}(dividend.high._value)
15991617
let shift = Builtin.zextOrBitCast_Int8_Int${dbits}(UInt8(${bits})._value)
16001618
let lhsHighShifted = Builtin.shl_Int${dbits}(lhsHigh, shift)
16011619
let lhsLow = Builtin.zext_Int${bits}_Int${dbits}(dividend.low._value)
1602-
let lhs_ = Builtin.or_Int${dbits}(lhsHighShifted, lhsLow)
1603-
let rhs_ = Builtin.${z}ext_Int${bits}_Int${dbits}(self._value)
1604-
1605-
let quotient_ = Builtin.${u}div_Int${dbits}(lhs_, rhs_)
1606-
let remainder_ = Builtin.${u}rem_Int${dbits}(lhs_, rhs_)
1620+
let a = Builtin.or_Int${dbits}(lhsHighShifted, lhsLow)
1621+
let b = Builtin.${z}ext_Int${bits}_Int${dbits}(self._value)
1622+
1623+
let q = Builtin.${u}div_Int${dbits}(a, b)
1624+
let r = Builtin.${u}rem_Int${dbits}(a, b)
16071625

1608-
let quotient = ${Self}(
1609-
Builtin.truncOrBitCast_Int${dbits}_Int${bits}(quotient_))
1610-
let remainder = ${Self}(
1611-
Builtin.truncOrBitCast_Int${dbits}_Int${bits}(remainder_))
1626+
% if signed:
1627+
// remainder is guaranteed representable, but the quotient might not be
1628+
// so we need to check that the conversion succeeds:
1629+
let (quotient, overflow) =
1630+
Builtin.s_to_s_checked_trunc_Int${dbits}_Int${bits}(q)
1631+
_precondition(!Bool(overflow), "Quotient is not representable")
1632+
% else:
1633+
// quotient and remainder are both guaranteed to be representable by
1634+
// the precondition we enforced above and the definition of division.
1635+
let quotient = Builtin.truncOrBitCast_Int${dbits}_Int${bits}(q)
1636+
% end
1637+
let remainder = Builtin.truncOrBitCast_Int${dbits}_Int${bits}(r)
16121638

1613-
return (quotient: quotient, remainder: remainder)
1614-
% end
1639+
return (quotient: Self(quotient), remainder: Self(remainder))
1640+
% end
16151641
}
1642+
% if bits == 64:
1643+
#endif
1644+
% end
16161645

16171646
/// A representation of this integer with the byte order swapped.
16181647
@_transparent

stdlib/public/core/Integers.swift

Lines changed: 161 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3383,6 +3383,17 @@ extension FixedWidthInteger {
33833383
//===--- UnsignedInteger --------------------------------------------------===//
33843384
//===----------------------------------------------------------------------===//
33853385

3386+
// Implementor's note: UnsignedInteger should have required Magnitude == Self,
3387+
// because it can necessarily represent the magnitude of every value and every
3388+
// recursive generic constraint should terminate unless there is a good
3389+
// semantic reason for it not to do so.
3390+
//
3391+
// However, we cannot easily add this constraint because it changes the
3392+
// mangling of generics constrained on <T: FixedWidthInteger & UnsignedInteger>
3393+
// to be <T: FixedWidthInteger where T.Magnitude == T>. As a practical matter,
3394+
// every unsigned type will satisfy this constraint, so converting between
3395+
// Magnitude and Self in generic code is acceptable.
3396+
33863397
/// An integer type that can represent only nonnegative values.
33873398
public protocol UnsignedInteger: BinaryInteger { }
33883399

@@ -3491,9 +3502,124 @@ extension UnsignedInteger where Self: FixedWidthInteger {
34913502
/// For unsigned integer types, this value is always `0`.
34923503
@_transparent
34933504
public static var min: Self { return 0 }
3505+
3506+
@_alwaysEmitIntoClient
3507+
public func dividingFullWidth(
3508+
_ dividend: (high: Self, low: Magnitude)
3509+
) -> (quotient: Self, remainder: Self) {
3510+
// Validate preconditions to guarantee that the quotient is representable.
3511+
precondition(self != .zero, "Division by zero")
3512+
precondition(dividend.high < self,
3513+
"Dividend.high must be smaller than divisor")
3514+
// UnsignedInteger should have a Magnitude = Self constraint, but does not,
3515+
// so we have to do this conversion (we can't easily add the constraint
3516+
// because it changes how generic signatures constrained to
3517+
// <FixedWidth & Unsigned> are minimized, which changes the mangling).
3518+
// In practice, "every" UnsignedInteger type will satisfy this, and if one
3519+
// somehow manages not to in a way that would break this conversion then
3520+
// a default implementation of this method never could have worked anyway.
3521+
let low = Self(dividend.low)
3522+
3523+
// The basic algorithm is taken from Knuth (TAoCP, Vol 2, §4.3.1), using
3524+
// words that are half the size of Self (so the dividend has four words
3525+
// and the divisor has two). The fact that the denominator has exactly
3526+
// two words allows for a slight simplification vs. Knuth's Algorithm D,
3527+
// in that our computed quotient digit is always exactly right, while
3528+
// in the more general case it can be one too large, requiring a subsequent
3529+
// borrow.
3530+
//
3531+
// Knuth's algorithm (and any long division, really), requires that the
3532+
// divisor (self) be normalized (meaning that the high-order bit is set).
3533+
// We begin by counting the leading zeros so we know how many bits we
3534+
// have to shift to normalize.
3535+
let lz = leadingZeroBitCount
3536+
3537+
// If the divisor is actually a power of two, division is just a shift,
3538+
// which we can handle much more efficiently. So we do a check for that
3539+
// case and early-out if possible.
3540+
if (self &- 1) & self == .zero {
3541+
let shift = Self.bitWidth - 1 - lz
3542+
let q = low &>> shift | dividend.high &<< -shift
3543+
let r = low & (self &- 1)
3544+
return (q, r)
3545+
}
3546+
3547+
// Shift the divisor left by lz bits to normalize it. We shift the
3548+
// dividend left by the same amount so that we get the quotient is
3549+
// preserved (we will have to shift right to recover the remainder).
3550+
// Note that the right shift `low >> (Self.bitWidth - lz)` is
3551+
// deliberately a non-masking shift because lz might be zero.
3552+
let v = self &<< lz
3553+
let uh = dividend.high &<< lz | low >> (Self.bitWidth - lz)
3554+
let ul = low &<< lz
3555+
3556+
// Now we have a normalized dividend (uh:ul) and divisor (v). Split
3557+
// v into half-words (vh:vl) so that we can use the "normal" division
3558+
// on Self as a word / halfword -> halfword division get one halfword
3559+
// digit of the quotient at a time.
3560+
let n_2 = Self.bitWidth/2
3561+
let mask = Self(1) &<< n_2 &- 1
3562+
let vh = v &>> n_2
3563+
let vl = v & mask
3564+
3565+
// For the (fairly-common) special case where vl is zero, we can simplify
3566+
// the arithmetic quite a bit:
3567+
if vl == .zero {
3568+
let qh = uh / vh
3569+
let residual = (uh &- qh &* vh) &<< n_2 | ul &>> n_2
3570+
let ql = residual / vh
3571+
3572+
return (
3573+
// Assemble quotient from half-word digits
3574+
quotient: qh &<< n_2 | ql,
3575+
// Compute remainder (we can re-use the residual to make this simpler).
3576+
remainder: ((residual &- ql &* vh) &<< n_2 | ul & mask) &>> lz
3577+
)
3578+
}
3579+
3580+
// Helper function: performs a (1½ word)/word division to produce a
3581+
// half quotient word q. We'll need to use this twice to generate the
3582+
// full quotient.
3583+
//
3584+
// high is the high word of the quotient for this sub-division.
3585+
// low is the low half-word of the quotient for this sub-division (the
3586+
// high half of low must be zero).
3587+
//
3588+
// returns the quotient half-word digit. In a more general setting, this
3589+
// computed digit might be one too large, which has to be accounted for
3590+
// later on (see Knuth, Algorithm D), but when the divisor is only two
3591+
// half-words (as here), that can never happen, because we use the full
3592+
// divisor in the check for the while loop.
3593+
func generateHalfDigit(high: Self, low: Self) -> Self {
3594+
// Get q̂ satisfying a = vh q̂ + r̂ with 0 ≤ r̂ < vh:
3595+
var (,) = high.quotientAndRemainder(dividingBy: vh)
3596+
// Knuth's "Theorem A" establishes that q̂ is an approximation to
3597+
// the quotient digit q, satisfying q ≤ q̂ ≤ q + 2. We adjust it
3598+
// downward as needed until we have the correct q.
3599+
while> mask || q̂ &* vl > (&<< n_2 | low) {
3600+
&-= 1
3601+
&+= vh
3602+
if> mask { break }
3603+
}
3604+
return
3605+
}
3606+
3607+
// Generate the first quotient digit, subtract off its product with the
3608+
// divisor to generate the residual, then compute the second quotient
3609+
// digit from that.
3610+
let qh = generateHalfDigit(high: uh, low: ul &>> n_2)
3611+
let residual = (uh &<< n_2 | ul &>> n_2) &- (qh &* v)
3612+
let ql = generateHalfDigit(high: residual, low: ul & mask)
3613+
3614+
return (
3615+
// Assemble quotient from half-word digits
3616+
quotient: qh &<< n_2 | ql,
3617+
// Compute remainder (we can re-use the residual to make this simpler).
3618+
remainder: ((residual &<< n_2 | ul & mask) &- (ql &* v)) &>> lz
3619+
)
3620+
}
34943621
}
34953622

3496-
34973623
//===----------------------------------------------------------------------===//
34983624
//===--- SignedInteger ----------------------------------------------------===//
34993625
//===----------------------------------------------------------------------===//
@@ -3622,6 +3748,40 @@ extension SignedInteger where Self: FixedWidthInteger {
36223748
// Having handled those special cases, this is safe.
36233749
return self % other == 0
36243750
}
3751+
3752+
@_alwaysEmitIntoClient
3753+
public func dividingFullWidth(
3754+
_ dividend: (high: Self, low: Magnitude)
3755+
) -> (quotient: Self, remainder: Self) {
3756+
// Get magnitude of dividend:
3757+
var magnitudeHigh = Magnitude(truncatingIfNeeded: dividend.high)
3758+
var magnitudeLow = dividend.low
3759+
if dividend.high < .zero {
3760+
let carry: Bool
3761+
(magnitudeLow, carry) = (~magnitudeLow).addingReportingOverflow(1)
3762+
magnitudeHigh = ~magnitudeHigh &+ (carry ? 1 : 0)
3763+
}
3764+
// Do division on magnitudes (using unsigned implementation):
3765+
let (unsignedQuotient, unsignedRemainder) = magnitude.dividingFullWidth(
3766+
(high: magnitudeHigh, low: magnitudeLow)
3767+
)
3768+
// Fixup sign: quotient is negative if dividend and divisor disagree.
3769+
// We will also trap here if the quotient does not fit in Self.
3770+
let quotient: Self
3771+
if self ^ dividend.high < .zero {
3772+
// It is possible that the quotient is representable but its magnitude
3773+
// is not representable as Self (if quotient is Self.min), so we have
3774+
// to handle that case carefully here.
3775+
precondition(unsignedQuotient <= Self.min.magnitude,
3776+
"Quotient is not representable.")
3777+
quotient = Self(truncatingIfNeeded: 0 &- unsignedQuotient)
3778+
} else {
3779+
quotient = Self(unsignedQuotient)
3780+
}
3781+
var remainder = Self(unsignedRemainder)
3782+
if dividend.high < .zero { remainder = 0 &- remainder }
3783+
return (quotient, remainder)
3784+
}
36253785
}
36263786

36273787
/// Returns the given integer as the equivalent value in a different integer

0 commit comments

Comments
 (0)