-
Notifications
You must be signed in to change notification settings - Fork 10.4k
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
Add default implementations for FixedWidthInteger.dividingFullWidth #70823
Add default implementations for FixedWidthInteger.dividingFullWidth #70823
Conversation
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 types 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.
@swift-ci test |
@swift-ci test |
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.
The algorithm looks spot on to me; I presume we already have tests that cover this?
// 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
omg
// 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 comment
The 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 comment
The reason will be displayed to describe this comment to others. Learn more.
So long as the outer function is specialized, yes.
// 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 |
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.
Most normal standard library tests won't exercise it because standard library integer types specialize the operation. I wrote a bunch of standalone tests for it, which I'll see about rolling into the test suite. |
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.
Nice explanations along the way!
- make these trap when quotient is not representable - use hardware divide for 64b integers on arm64_32 - defer to new default implementation - add some test coverage
@swift-ci test |
@swift-ci benchmark |
(We don't expect benchmarks to move much from this, but it's good to make sure.) |
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.
I love it!
As expected, benchmarks are just noisy tests, nothing related to division. Importantly, no noticeable code size changes at all. |
…wiftlang#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.
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
Fixes #54509.