-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Add default implementations for FixedWidthInteger.dividingFullWidth #70823
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
b25ea5b
b5b663e
6e8eda7
1b22fc8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -3383,6 +3383,17 @@ extension FixedWidthInteger { | |
//===--- UnsignedInteger --------------------------------------------------===// | ||
//===----------------------------------------------------------------------===// | ||
|
||
// Implementor's note: UnsignedInteger should have required Magnitude == Self, | ||
// because it can necessarily represent the magnitude of every value and every | ||
// recursive generic constraint should terminate unless there is a good | ||
// semantic reason for it not to do so. | ||
// | ||
// However, we cannot easily add this constraint because it changes the | ||
// mangling of generics constrained on <T: FixedWidthInteger & UnsignedInteger> | ||
// to be <T: FixedWidthInteger where T.Magnitude == T>. As a practical matter, | ||
// every unsigned type will satisfy this constraint, so converting between | ||
// Magnitude and Self in generic code is acceptable. | ||
|
||
/// An integer type that can represent only nonnegative values. | ||
public protocol UnsignedInteger: BinaryInteger { } | ||
|
||
|
@@ -3491,9 +3502,124 @@ extension UnsignedInteger where Self: FixedWidthInteger { | |
/// For unsigned integer types, this value is always `0`. | ||
@_transparent | ||
public static var min: Self { return 0 } | ||
|
||
@_alwaysEmitIntoClient | ||
public func dividingFullWidth( | ||
_ dividend: (high: Self, low: Magnitude) | ||
) -> (quotient: Self, remainder: Self) { | ||
// Validate preconditions to guarantee that the quotient is representable. | ||
precondition(self != .zero, "Division by zero") | ||
precondition(dividend.high < self, | ||
"Dividend.high must be smaller than divisor") | ||
// UnsignedInteger should have a Magnitude = Self constraint, but does not, | ||
// so we have to do this conversion (we can't easily add the constraint | ||
// because it changes how generic signatures constrained to | ||
// <FixedWidth & Unsigned> are minimized, which changes the mangling). | ||
// In practice, "every" UnsignedInteger type will satisfy this, and if one | ||
// somehow manages not to in a way that would break this conversion then | ||
// a default implementation of this method never could have worked anyway. | ||
let low = Self(dividend.low) | ||
|
||
// The basic algorithm is taken from Knuth (TAoCP, Vol 2, §4.3.1), using | ||
// words that are half the size of Self (so the dividend has four words | ||
// and the divisor has two). The fact that the denominator has exactly | ||
// two words allows for a slight simplification vs. Knuth's Algorithm D, | ||
// in that our computed quotient digit is always exactly right, while | ||
// in the more general case it can be one too large, requiring a subsequent | ||
// borrow. | ||
// | ||
// Knuth's algorithm (and any long division, really), requires that the | ||
// divisor (self) be normalized (meaning that the high-order bit is set). | ||
// We begin by counting the leading zeros so we know how many bits we | ||
// have to shift to normalize. | ||
let lz = leadingZeroBitCount | ||
|
||
// If the divisor is actually a power of two, division is just a shift, | ||
// which we can handle much more efficiently. So we do a check for that | ||
// case and early-out if possible. | ||
if (self &- 1) & self == .zero { | ||
let shift = Self.bitWidth - 1 - lz | ||
let q = low &>> shift | dividend.high &<< -shift | ||
let r = low & (self &- 1) | ||
return (q, r) | ||
} | ||
|
||
// Shift the divisor left by lz bits to normalize it. We shift the | ||
// dividend left by the same amount so that we get the quotient is | ||
// preserved (we will have to shift right to recover the remainder). | ||
// Note that the right shift `low >> (Self.bitWidth - lz)` is | ||
// deliberately a non-masking shift because lz might be zero. | ||
let v = self &<< lz | ||
let uh = dividend.high &<< lz | low >> (Self.bitWidth - lz) | ||
let ul = low &<< lz | ||
|
||
// Now we have a normalized dividend (uh:ul) and divisor (v). Split | ||
// v into half-words (vh:vl) so that we can use the "normal" division | ||
// on Self as a word / halfword -> halfword division get one halfword | ||
// digit of the quotient at a time. | ||
let n_2 = Self.bitWidth/2 | ||
let mask = Self(1) &<< n_2 &- 1 | ||
let vh = v &>> n_2 | ||
let vl = v & mask | ||
|
||
// For the (fairly-common) special case where vl is zero, we can simplify | ||
// the arithmetic quite a bit: | ||
if vl == .zero { | ||
let qh = uh / vh | ||
let residual = (uh &- qh &* vh) &<< n_2 | ul &>> n_2 | ||
let ql = residual / vh | ||
|
||
return ( | ||
// Assemble quotient from half-word digits | ||
quotient: qh &<< n_2 | ql, | ||
// Compute remainder (we can re-use the residual to make this simpler). | ||
remainder: ((residual &- ql &* vh) &<< n_2 | ul & mask) &>> lz | ||
) | ||
} | ||
|
||
// Helper function: performs a (1½ word)/word division to produce a | ||
// half quotient word q. We'll need to use this twice to generate the | ||
// full quotient. | ||
// | ||
// high is the high word of the quotient for this sub-division. | ||
// low is the low half-word of the quotient for this sub-division (the | ||
// high half of low must be zero). | ||
// | ||
// returns the quotient half-word digit. In a more general setting, this | ||
// computed digit might be one too large, which has to be accounted for | ||
// later on (see Knuth, Algorithm D), but when the divisor is only two | ||
// half-words (as here), that can never happen, because we use the full | ||
// divisor in the check for the while loop. | ||
func generateHalfDigit(high: Self, low: Self) -> Self { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I presume this as an inner function is 100% specialized and inlined There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. So long as the outer function is specialized, yes. |
||
// Get q̂ satisfying a = vh q̂ + r̂ with 0 ≤ r̂ < vh: | ||
var (q̂, r̂) = high.quotientAndRemainder(dividingBy: vh) | ||
// Knuth's "Theorem A" establishes that q̂ is an approximation to | ||
// the quotient digit q, satisfying q ≤ q̂ ≤ q + 2. We adjust it | ||
// downward as needed until we have the correct q. | ||
while q̂ > mask || q̂ &* vl > (r̂ &<< n_2 | low) { | ||
q̂ &-= 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. ❤️ the use of circumflex There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. omg |
||
r̂ &+= vh | ||
if r̂ > mask { break } | ||
} | ||
return q̂ | ||
} | ||
|
||
// Generate the first quotient digit, subtract off its product with the | ||
// divisor to generate the residual, then compute the second quotient | ||
// digit from that. | ||
let qh = generateHalfDigit(high: uh, low: ul &>> n_2) | ||
let residual = (uh &<< n_2 | ul &>> n_2) &- (qh &* v) | ||
let ql = generateHalfDigit(high: residual, low: ul & mask) | ||
|
||
return ( | ||
// Assemble quotient from half-word digits | ||
quotient: qh &<< n_2 | ql, | ||
// Compute remainder (we can re-use the residual to make this simpler). | ||
remainder: ((residual &<< n_2 | ul & mask) &- (ql &* v)) &>> lz | ||
) | ||
} | ||
} | ||
|
||
|
||
//===----------------------------------------------------------------------===// | ||
//===--- SignedInteger ----------------------------------------------------===// | ||
//===----------------------------------------------------------------------===// | ||
|
@@ -3622,6 +3748,40 @@ extension SignedInteger where Self: FixedWidthInteger { | |
// Having handled those special cases, this is safe. | ||
return self % other == 0 | ||
} | ||
|
||
@_alwaysEmitIntoClient | ||
public func dividingFullWidth( | ||
_ dividend: (high: Self, low: Magnitude) | ||
) -> (quotient: Self, remainder: Self) { | ||
// Get magnitude of dividend: | ||
var magnitudeHigh = Magnitude(truncatingIfNeeded: dividend.high) | ||
var magnitudeLow = dividend.low | ||
if dividend.high < .zero { | ||
let carry: Bool | ||
(magnitudeLow, carry) = (~magnitudeLow).addingReportingOverflow(1) | ||
magnitudeHigh = ~magnitudeHigh &+ (carry ? 1 : 0) | ||
} | ||
// Do division on magnitudes (using unsigned implementation): | ||
let (unsignedQuotient, unsignedRemainder) = magnitude.dividingFullWidth( | ||
(high: magnitudeHigh, low: magnitudeLow) | ||
) | ||
// Fixup sign: quotient is negative if dividend and divisor disagree. | ||
// We will also trap here if the quotient does not fit in Self. | ||
let quotient: Self | ||
if self ^ dividend.high < .zero { | ||
// It is possible that the quotient is representable but its magnitude | ||
// is not representable as Self (if quotient is Self.min), so we have | ||
// to handle that case carefully here. | ||
precondition(unsignedQuotient <= Self.min.magnitude, | ||
"Quotient is not representable.") | ||
quotient = Self(truncatingIfNeeded: 0 &- unsignedQuotient) | ||
} else { | ||
quotient = Self(unsignedQuotient) | ||
} | ||
var remainder = Self(unsignedRemainder) | ||
if dividend.high < .zero { remainder = 0 &- remainder } | ||
return (quotient, remainder) | ||
} | ||
} | ||
|
||
/// Returns the given integer as the equivalent value in a different integer | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
depending on the register spill here; would it be worthwhile to optimize by adding additional variables like something to represent the masked value of
ul | mask
etc?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It ought not to matter, so I'll hold off until we see a case where it does.